В качестве небольшого экскурса для непосвященных в логическое программирование
в этом тексте будетпроведен сеанс магии с разоблачением приведен подход
к созданию REST-сервера и замерены его параметры.
В чем подвох? А все просто — будем делать на Прологе (в реализации SWI-prolog)…
Итак, что нужно. Во-первых скачать (если еще нет) и поставить собственно swi-prolog
с его сайта www.swi-prolog.org. Проще создать небольшой файл типа rest.pl с начальным содержимым типа
(примеры приведены для консоли linux, но не сильно отличаются от windows):
После этого запустится emacs-подобный редактор и можно все набирать в нем.
Итак, в начале все же укажем что мы будем пользоваться библиотеками и напишем что значит
запуск сервера — в прологе символы :- можно читать как «это есть»:
Как видно, мы планируем что сервис будет отвечать на методы get и post.
Теперь собственно, что мы будем делать. По запросу get будем отвечать страницей с полем ввода числа, при его вводе будем запрашивать метод post и вычислять это число Фиббоначии.
То есть:
Ну вот и все! Мы уже на самом деле написали наш сервер… Пролог же декларативный язык — вот мы и декларировали варианты обработки. Кстати, язык можно использовать любой — я решил местами писать на русском.
Но погодите, что же он делает? Ведь 'форма', 'ответить' и 'обсчитать' — это наши предикаты и они не определены пока. Давайте это исправим:
Ну и приведем два варианта для обсчитать — пусть у нас числа Фиббоначчи только положительные будут:
Ну и осталось определить собственно что такое число Фиббоначи:
Это определение, конечно,не по фен-шую не совсем привычное, зато считается чуть ли не быстрее чем если бы мы писали на С…
Итак, будет ли это работать? Проверяем:
Так, сервер вроде стартовал. Кстати, он многопоточный! Для проверки нужно открыть адрес
127.0.0.1:8080/ и ввести какое-нибудь число — например, 1000:
Небольшое исследование производительности сервера — проверим метод GET (POST очевидно сильно зависит от заданного числа, так 10000000 число считается, конечно, но несколько секунд...)
Что же 14140 запросов в секунду при 10 потоках — это очень неплохо для обычного компа!
И да, пролог имеет такую возможность — логический вид. Так что если вы поменяете что-то в коде, то вам достаточно в консоли набрать
И все ваши изменения будут работать в новых запросах — ничего не нужно перегружать, останавливать и т.д.
Надеюсь, вам было интересно посмотреть, как создают rest-сервера на таком простом примере. Конечно, можно описывать rest интерфейс статически, как и сделано в примере, можно вводить всякие переменные, использовать часть пути URL как переменные — ну в общем все как обычно.
Можно делать это динамически, менять логику работы программы создавая, удаляя и модифицируя предикаты — этакий самомодифицирующийся сервер, чья работа зависит от истории. Можно подключать базы данных. В общем, все как обычно, только работать просто и приятно.
в этом тексте будет
к созданию 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 как переменные — ну в общем все как обычно.
Можно делать это динамически, менять логику работы программы создавая, удаляя и модифицируя предикаты — этакий самомодифицирующийся сервер, чья работа зависит от истории. Можно подключать базы данных. В общем, все как обычно, только работать просто и приятно.
Комментарии (9)
oam2oam Автор
15.10.2019 18:46Спасибо! На самом деле, конечно, именно пролог в каком-то смысле один из лучших кандидатов — в нем в единственном реальная иммутабельность переменных (ну там тоже есть способ это обойти, но не простой), да и других удивительных возможностей немало…
trapwalker
16.10.2019 14:35Никогда не понимал почему разработчики всяческих RPG (игр) не используют пролог для декларативного описания квестов и обработки их логики в ходе игры.
А тут вон оно еще что бывает...
Sly_tom_cat
Неожиданное применение языка (по крайней мере для меня).
У меня с институтской поры осталось шаблонное впечатление что пролог это только для решения логических задач связанных с выводами на основе фактов и наборов правил.
Вы сломали этот шаблон, за что вам — спасибо!
Homas
Чтобы ещё больше сломать посмотри на cowboy — http сервер написанный на Erlang.
Вот мой DNS сервер с REST API (использую cowboy) на Erlang. Да и вообще историю Erlang почитайте для чего он разрабатывался.
begemot_sun
Судя по readme ваш DNS-сервер выглядит законченным продуктом.
Вы на нём зарабатываете как-то?
Homas
В данный момент — нет. Как и любой open source продукт монетизация осуществляется только за счет консалтинга/поддержки, «пожертвований» и построение сервисов вокруг него (в этом плане я двигаю ioc2rpz community).
+ Продукт ориентирован на профи, средний и большой enterprise. Потенциал есть, но нужно значительно больше времени (чего нет) тратить на продвижение ну и потом на предоставление «услуг». Пока из публичных мероприятий я демонстрировал его на 2х DefCon и BlackHat.
Из неявного (что возможно оказывает больший profit) — «делаю себе имя» на данном продукте. :)