Upload
eugene-tataurov
View
322
Download
1
Embed Size (px)
Citation preview
Erlang и n2o web-разработка без
JavaScript
DevelCamp 2014 Татауров Евгений, Noda
Erlang• Функциональный
• Динамически типизирован
• Лямбды
• Pattern matching
lists:map(fun(X) -> X*2 end, [4,8,15,16,23,42]).
double([H|T]) -> [2*H|double(T)];double([]) -> [].
Erlang
• Процессы-акторы
• Message passing
• Распределенность из коробки
Erlang
• Let it crash
• OTP
• Supervision tree
n2o
• Cowboy (HTTP & WebSockets)
• Генерация HTML и обработчиков
• Scala Lift
• deferred javascript
n2o Elements#panel{id=Panel, body=#button{id=myButton, body= <<"OK">>, postback={ok, <<"INFO">>}}}
n2o Elements#panel{id=Panel, body=#button{id=myButton, body= <<"OK">>, postback={ok, <<"INFO">>}}}
<div id="myPanel"><button id="myButton" type="button">OK</button></div>
n2o Elements#panel{id=Panel, body=#button{id=myButton, body= <<"OK">>, postback={ok, <<"INFO">>}}}
<div id="myPanel"><button id="myButton" type="button">OK</button></div>document.getElementById('myButton').addEventListener('click',function (event){
ws.send(enc(tuple(atom('pickle'),bin('myButton'),bin('g2gCaAVkAAJldmQABWluZGV4aAJkAAJva20AAAAESU5GT2sACG15QnV0dG9uZAAFZXZlbnRoA2IAAAWFYgAKp5tiAACpXA=='),[tuple(tuple(utf8_toByteArray('myButton'), bin('detail')), event.detail)])));
})
Мини-приложение + | +--------------+-+ | +----------------+ | web_app | instagram_app +-+--------------+ | | | WS | index | | | | +----+ | | | | Instagram | | | | | +----+ | | | | | | | | | +--------> +---+ | +---------------------+ HTTP POST | +^ +-+ +--------------+-+ | | | +----------------+ +----------------+ | | | <----------+ || +++--------------+ | | | | | |-------| loop | | | | | <----------+ +-------+> | | | | | | | | | | | | <---------+ | | | | | | | | <+-----------+ | | +-----------+ | | +-+ | | | +----------------+ | | | | +---------------------+ |
index.erlmain() -> #dtl{file="index", bindings=[{body, body()}]}.body() -> [#panel{id=butPanel, body=[ #button{id=catsButton, class= <<"btn">>, body= <<"show #cats”>>, postback={tag,<<"cats">>}}
]}, #panel{id=updater}].
index.erl
event({tag, Tag}) -> wf:update(butPanel, #h1{body= << <<"#">>/binary, Tag/binary>>}), {ok, _Pid} = wf:async(Tag, fun() -> loop(updater, Tag) end), manager:subscribe(Tag), wf:reg(Tag).
index.erlloop(Elem, Tag) -> receive {gproc_ps_event, {tag, Tag}, Text} -> insert_image(Elem, Text), wf:flush(Tag); timer:sleep(500), loop(Elem, Tag) end.
insert_image(Elem, Img) -> wf:insert_top(Elem, #panel{body=#panel{body=[ #link{href=maps:get(<<"link">>, Img), body=#image{src=maps:get(<<"url">>, maps:get(<<"low_resolution">>, maps:get(<<"images">>, Img))), }}]}}).
Демо
Вариант на Python
• asyncio (aiohttp + websockets)
• акторы, общение через очередь
• код на JavaScript
@asyncio.coroutinedef handler(websocket, _path): new_image_receiver = ProcessQueue() SubscriptionManager.subscribe(TAG, new_image_receiver) while True: message = yield from new_image_receiver.receive() if not websocket.open: SubscriptionManager.unsubscribe(TAG, new_image_receiver) break yield from websocket.send(json.dumps(message))
var socket = new WebSocket("ws://127.0.0.1:8765");socket.onmessage = function (event) { var msg = JSON.parse(event.data); var img = document.createElement('img'); img.src = msg.images.low_resolution.url; var pics = document.getElementById('pics'); pics.insertBefore(img, pics.firstChild);}
Производительность
Req/sec Latency
n2o 12450 21
aiohttp 530 117
tornado 1500 580
nginx 17600 49
wrk -t4 -c1000 -d30s http://127.0.0.1:8080
Выводы• n2o быстр, как в плане работы, так и в плане разработки
• Можно не писать HTML и JavaScript
• Гибкость. Толстый клиент и REST endpoint, либо умный сервер и простой клиент.
• Все плюсы Erlang
Выводы
• Свой DSL
• Нет нормальной документации API (но есть Nitrogen API)
• Сыроват, меняется, нет тестов.
Ресурсы• http://synrc.com/apps/n2o/doc/web
• http://nitrogenproject.com
• Learn You Some Erlang for Great Good!
• Programming Erlang
• Erlang плагин для IDEA http://ignatov.github.io/intellij-erlang/
• https://github.com/etataurov/instastream