REST сервер на Прологе, как это выглядит?

в 13:40, , рубрики: Prolog, rest-server, Программирование

В качестве небольшого экскурса для непосвященных в логическое программирование
в этом тексте будет проведен сеанс магии с разоблачением приведен подход
к созданию REST-сервера и замерены его параметры.

В чем подвох? А все просто — будем делать на Прологе (в реализации SWI-prolog)…

Итак, что нужно. Во-первых скачать (если еще нет) и поставить собственно swi-prolog
с его сайта www.swi-prolog.org. Проще создать небольшой файл типа rest.pl с начальным содержимым типа

сервер.

и дальше загрузить его и редактировать уже средствами самой пролог-системы, вот так
(примеры приведены для консоли linux, но не сильно отличаются от windows):

  $echo "сервер." > rest.pl
  $swipl
   ...
  ?- [rest].
  true.

  ?- edit(сервер).

После этого запустится emacs-подобный редактор и можно все набирать в нем.

Итак, в начале все же укажем что мы будем пользоваться библиотеками и напишем что значит
запуск сервера — в прологе символы :- можно читать как «это есть»:

:- use_module(library(http/http_server)).
:- use_module(library(http/http_json)).
:- http_handler(/,сервис(M), [method(M),methods([get,post]),time_limit(10000)]).

сервер :- http_server(http_dispatch, [port(8080)]).

Как видно, мы планируем что сервис будет отвечать на методы get и post.

Модельная задача

Теперь собственно, что мы будем делать. По запросу get будем отвечать страницей с полем ввода числа, при его вводе будем запрашивать метод post и вычислять это число Фиббоначии.

То есть:

сервис(get,_) :- форма(X),ответить(X).
сервис(post,Запрос) :-
  http_parameters(Запрос,[val(Число,[integer])]),
  обсчитать(Число).

Ну вот и все! Мы уже на самом деле написали наш сервер… Пролог же декларативный язык — вот мы и декларировали варианты обработки. Кстати, язык можно использовать любой — я решил местами писать на русском.

Но погодите, что же он делает? Ведь 'форма', 'ответить' и 'обсчитать' — это наши предикаты и они не определены пока. Давайте это исправим:

ответить(Вставка):-
 format('Content-type: text/html~n~n
 <html><body>Вычисление чисел Фиббоначчи<br/>
 ~w
 </body></html>~n', [Вставка]).

форма('<form method="POST"><input name="val"/></form>').

Ну и приведем два варианта для обсчитать — пусть у нас числа Фиббоначчи только положительные будут:

обсчитать(Число):-
  Число > 0,
  фиббоначи(0,1,1,Число,Ответ),форма(X),
  format(atom(Строка),'~w число Фиббоначи равно ~w<br/>~w',[Число,Ответ,X]),
  ответить(Строка).
обсчитать(Число):- 
  форма(X),
  format(atom(Строка),'заданное число ~w меньше 0<br/>~w',[Число,X]),
  ответить(Строка).

Ну и осталось определить собственно что такое число Фиббоначи:

фиббоначи(_F, F1, N, N, F1) :- !.
фиббоначи(F0, F1, I, N, F) :-
        F2 is F0+F1,
        I2 is I + 1,
        !,фиббоначи(F1, F2, I2, N, F).

Это определение, конечно, не по фен-шую не совсем привычное, зато считается чуть ли не быстрее чем если бы мы писали на С…

Итак, будет ли это работать? Проверяем:

 ?- сервер.
 % Started server at http://localhost:8080/
 true.

Так, сервер вроде стартовал. Кстати, он многопоточный! Для проверки нужно открыть адрес
127.0.0.1:8080/ и ввести какое-нибудь число — например, 1000:

Вычисление чисел Фиббоначчи
1000 число Фиббоначи равно
4346655768693745643568852767504062580
2564660517371780402481729089536555417
9490518904038798400792551692959225930
8032263477520968962323987332247116164
2996440906533187938298969649928516003
704476137795166849228875

Ну что же, оно работает!

Небольшое исследование производительности сервера — проверим метод GET (POST очевидно сильно зависит от заданного числа, так 10000000 число считается, конечно, но несколько секунд...)

 $ ab -k -c 4 -n 4000 http://127.0.0.1:8080/

...
Concurrency Level:      4
Time taken for tests:   0.283 seconds
Complete requests:      4000
Failed requests:        0
Keep-Alive requests:    4000
Total transferred:      1108000 bytes
HTML transferred:       544000 bytes
Requests per second:    14140.57 [#/sec] (mean)
Time per request:       0.283 [ms] (mean)
Time per request:       0.071 [ms] (mean, across all concurrent requests)
Transfer rate:          3825.14 [Kbytes/sec] received
...

Что же 14140 запросов в секунду при 10 потоках — это очень неплохо для обычного компа!

И да, пролог имеет такую возможность — логический вид. Так что если вы поменяете что-то в коде, то вам достаточно в консоли набрать

 ?- make.

И все ваши изменения будут работать в новых запросах — ничего не нужно перегружать, останавливать и т.д.

Надеюсь, вам было интересно посмотреть, как создают rest-сервера на таком простом примере. Конечно, можно описывать rest интерфейс статически, как и сделано в примере, можно вводить всякие переменные, использовать часть пути URL как переменные — ну в общем все как обычно.

Можно делать это динамически, менять логику работы программы создавая, удаляя и модифицируя предикаты — этакий самомодифицирующийся сервер, чья работа зависит от истории. Можно подключать базы данных. В общем, все как обычно, только работать просто и приятно.

Автор: Олег Морозов

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js