Про HTTP серверы: можно ещё быстрее

479 views
Skip to first unread message

Max Lapshin

unread,
Jan 4, 2012, 4:05:22 AM1/4/12
to erlang-...@googlegroups.com
Лев Валкин рассказывал (http://lionet.livejournal.com/84884.html) о
том, как делал свой HTTP сервер, потому что имеющихся возможностей
эрланга стало не хватать.

Я столкнулся с похожими проблемами, когда возник вопрос про раздачу
более гигабита. Реализация HTTP в эрланге на многих десятках тысяч
запросов в минуту не оптимальна. Основная проблема мне видится в том,
что ошметки запроса (заголовки) по одному гоняются из драйвера в код и
там аккумуллируются.

Итог простой: раздача гигабита (сырая ретрансляция) требует больше
одного ядра, что выглядит плохо на фоне varnish, которому требуется на
это не более 30%.

Когда Лев рассказывал про свой косер, я ему сказал, что мне видится
его архитектура неудачной с той точки зрения, что внутри эрланга уже
есть свой libevent со всем чем только нужно и хороший API к нему в
виде драйверов. Если бы он свой косер написал в виде драйвера, то всё
работало бы ничуть не хуже, но при этом в одном адресном пространстве
с бизнес-логикой.

Я решил провести эксперимент и спустить обработку HTTP вниз в драйвер.
Взял http_parser.c, который был выпилен из nginx и используется в
Node.js и воткнул его в эрланговый драйвер.
Получилось неплохо: http://github.com/erlyvideo/microtcp

Итог такой: этот сырой прототип раздает гигабит (./test.erl + ab)
примерно в 20-30% ядра. Ровно такой же бенчмарк на обычном эрланговом
HTTP-сервере показывает около 100%. На основании этого можно сделать
вывод о том, что действительно обработка самого протокола силами
эрланга приводит к ужасной потере скорости и развивать данный кусок
кода стоит.

Ghost

unread,
Jan 4, 2012, 4:18:08 AM1/4/12
to erlang-...@googlegroups.com
а может быть дешевле по трудозатратам просто распараллелить на несколько нод?

Max Lapshin

unread,
Jan 4, 2012, 4:26:24 AM1/4/12
to erlang-...@googlegroups.com
2012/1/4 Ghost <halt...@gmail.com>:

> а может быть дешевле по трудозатратам просто распараллелить на несколько нод?
>

Слова «эрланг параллелится по нодам» многие люди воспринимают как
волшебную мантру, которая вылечит от любой проблемы.
Что вы собрались параллелить? Шторм из HTTP запросов?

Taras Halturin

unread,
Jan 4, 2012, 4:37:28 AM1/4/12
to erlang-...@googlegroups.com
ставим перед ерланговским набором машин пару nginx'ов и размазываем нагрузку. Зачем втаскивать в аппликейшн функционал фронта? Нет, чисто академически идея ясна, но с практической точки зрения, обычно проще и дешевле разделять зоны "ответственности", чем пытаться втиснуть в один котел и потом придаваться диву, какже быстро варится кашка в нем :). Главный вопрос (лично для меня) в таких случаях - а надо ли оно? ;)

PS: разумеется, нужно владеть контекстом, чтобы делать корректные выводы. сейчас же я меряю "общим знаменателем". возможно ваш случай именно тот, где нужно тащить обработку HTTP на более нижний уровень. Я вот тоже подумываю libgsoap затащить в драйвер... но это так, планы-планы, дюже время жалко да и нужда пока не сильно приспичила. 

2012/1/4 Max Lapshin <max.l...@gmail.com>

--
Страница рассылки: http://groups.google.com/group/erlang-russian
Jabber-
конференция: erl...@conference.jabber.ru
Новости: http://erlanger.ru
Написать письмо: erlang-...@googlegroups.com
Отписаться: erlang-russia...@googlegroups.com



--
With Best Regards.
Taras Halturin

Max Lapshin

unread,
Jan 4, 2012, 4:53:31 AM1/4/12
to erlang-...@googlegroups.com
2012/1/4 Taras Halturin <halt...@gmail.com>:

> ставим перед ерланговским набором машин пару nginx'ов и размазываем нагрузку.

А потом, получив двухкратную нагрузку на CPU вдруг вспоминаем, что
оказывается nginx не умеет склеивать два и более одновременных
запросов к бекенду и не умеет кешировать контент в памяти.

Опачки, какая незадача! А ведь так всё в блогах красиво расписывают!

А заодно пишем долгие и подробные инструкции админам о том, как же
помимо сервера ставить ещё и кеши к нему. И ловим кучу проблем с тем,
что админы сделали что-то не так, а разгребать это приходится не им.

Taras Halturin

unread,
Jan 4, 2012, 5:03:42 AM1/4/12
to erlang-...@googlegroups.com
А что значит "склеивать"?
На счет "не умеет кешировать" - неправда ваша, либо хотите от веб-сервера возможностей аппликейшн части. 
Последний абзац вообще не имеет ничего общего с технической частью - это проблемы коммуникации :). 



2012/1/4 Max Lapshin <max.l...@gmail.com>
--
Страница рассылки: http://groups.google.com/group/erlang-russian
Jabber-
конференция: erl...@conference.jabber.ru
Новости: http://erlanger.ru
Написать письмо: erlang-...@googlegroups.com
Отписаться: erlang-russia...@googlegroups.com

JLarky

unread,
Jan 4, 2012, 6:11:40 AM1/4/12
to Erlang в России
Меня немного смущает что https://github.com/erlyvideo/microtcp это tcp
а не http, поясните бога ради в какой таки роли это надо использовать?
Вместо gen_tcp или вместо чего-то вроде webmachine?

Gleb Peregud

unread,
Jan 4, 2012, 6:14:49 AM1/4/12
to erlang-...@googlegroups.com
2012/1/4 JLarky <jla...@gmail.com>:

> Меня немного смущает что https://github.com/erlyvideo/microtcp это tcp
> а не http, поясните бога ради в какой таки роли это надо использовать?
> Вместо gen_tcp или вместо чего-то вроде webmachine?

На сколько я понял код и Макса, то вместо gen_tcp с packet={http|httph}

Max Lapshin

unread,
Jan 4, 2012, 7:16:07 AM1/4/12
to erlang-...@googlegroups.com
2012/1/4 Gleb Peregud <gleb...@gmail.com>:

>
> На сколько я понял код и Макса, то вместо gen_tcp с packet={http|httph}
>

Совершенно верно. Вместо gen_tcp + http.

Как я уже объяснил: парсинг HTTP запросов достаточно дорогая вещь,
поэтому имеет смысл убрать это из эрланга.
Текущая реализация в эрланге недостаточно хороша, видимо из-за того,
что гоняет хедеры отдельными сообщениями.

Yurii Rashkovskii

unread,
Jan 4, 2012, 7:19:04 AM1/4/12
to erlang-...@googlegroups.com
Насколько я понимаю, эта штука должна быть весьма вкручиваема в
cowboy, благодаря его модульности.

2012/1/4 Max Lapshin <max.l...@gmail.com>:

Max Lapshin

unread,
Jan 4, 2012, 7:29:10 AM1/4/12
to erlang-...@googlegroups.com
2012/1/4 Yurii Rashkovskii <yra...@gmail.com>:

> Насколько я понимаю, эта штука должна быть весьма вкручиваема в
> cowboy, благодаря его модульности.
>

https://github.com/extend/cowboy/issues/121

Но тут есть небольшая тонкость.

Дело в том, что http_parser.c берет на себя половину HTTP, в частности
такие штуки как keepalive и т.п. и работает
сильно удобнее в том плане, что сразу присылает все заголовки, не надо
накапливать и возиться с этим.

Но кое чего из cowboy_http_request хочется повторно использовать и тут
возникают некоторые вопросы.

Dmitry Groshev

unread,
Jan 4, 2012, 7:58:41 AM1/4/12
to erlang-...@googlegroups.com
Макс, очень круто. Ты писал в англоязычную рассылку?

4 января 2012 г. 16:29 пользователь Max Lapshin <max.l...@gmail.com> написал:

Max Lapshin

unread,
Jan 4, 2012, 8:05:44 AM1/4/12
to erlang-...@googlegroups.com
2012/1/4 Dmitry Groshev <lambda...@gmail.com>:

> Макс, очень круто. Ты писал в англоязычную рассылку?
>

Ещё нет. Поправлю проблемы со стабильностью и напишу.

Цель очень простая: сделать так, что бы веб-сервер на эрланге был не
более чем в полтора-два раза тормозной чем варниш.

Dmitrii Dimandt

unread,
Jan 4, 2012, 10:39:44 AM1/4/12
to erlang-...@googlegroups.com
Если такое получится сделать, то оооо, *экстаз* :)

2012/1/4 Max Lapshin <max.l...@gmail.com>

Max Lapshin

unread,
Jan 4, 2012, 10:39:55 AM1/4/12
to erlang-...@googlegroups.com
# ./httperf --port=9000 --uri=/index.html --recv-buffer=165536
--num-conns=1000 --num-calls=200 --rate=7000
httperf --client=0/1 --server=localhost --port=9000 --uri=/index.html
--rate=7000 --send-buffer=4096 --recv-buffer=165536 --num-conns=1000
--num-calls=200
Maximum connect burst length: 14

Total: connections 1000 requests 200000 replies 200000 test-duration 19.127 s

Connection rate: 52.3 conn/s (19.1 ms/conn, <=1000 concurrent connections)
Connection time [ms]: min 8970.4 avg 15117.7 max 19031.9 median
16612.5 stddev 3052.6
Connection time [ms]: connect 2994.2
Connection length [replies/conn]: 200.000

Request rate: 10456.2 req/s (0.1 ms/req)
Request size [B]: 72.0

Reply rate [replies/s]: min 10254.1 avg 10851.1 max 12013.6 stddev
1006.9 (3 samples)
Reply time [ms]: response 49.8 transfer 10.8
Reply size [B]: header 67.0 content 130000.0 footer 0.0 (total 130067.0)
Reply status: 1xx=0 2xx=200000 3xx=0 4xx=0 5xx=0

CPU time [s]: user 1.34 system 17.46 (user 7.0% system 91.3% total 98.3%)
Net I/O: 1328860.5 KB/s (10886.0*10^6 bps)

Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0

Осталось только повторить на реальном железе: Net I/O: (10886.0*10^6 bps)

Dmitry Vasiliev

unread,
Jan 4, 2012, 12:38:20 PM1/4/12
to erlang-...@googlegroups.com
On 04.01.2012 16:16, Max Lapshin wrote:
> 2012/1/4 Gleb Peregud<gleb...@gmail.com>:
>> О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫, О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ gen_tcp О©╫ packet={http|httph}
>
> О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫. О©╫О©╫О©╫О©╫О©╫О©╫ gen_tcp + http.
>
> О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫: О©╫О©╫О©╫О©╫О©╫О©╫О©╫ HTTP О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫,
> О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫ О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫.
> О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫, О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫-О©╫О©╫ О©╫О©╫О©╫О©╫,
> О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫.

IMHO О©╫О©╫О©╫О©╫О©╫ packet={http|httph} О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ -
О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫ О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ HTTP О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫. О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫
О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ erlang:decode_packet/3.

--
Dmitry Vasiliev <dima at hlabs.org>
http://hlabs.org
http://twitter.com/hdima

Slav Pankratov

unread,
Jan 4, 2012, 12:58:20 PM1/4/12
to erlang-...@googlegroups.com
Такая штука будет одинаково полезна и для раздачи видео, и для соц игр
- или есть ньюансы?

thanks,
Slav

4 января 2012 г. 20:38 пользователь Dmitry Vasiliev <di...@hlabs.org> написал:


> On 04.01.2012 16:16, Max Lapshin wrote:
>>
>> 2012/1/4 Gleb Peregud<gleb...@gmail.com>:
>>>

>>> На сколько я понял код и Макса, то вместо gen_tcp с packet={http|httph}
>>
>>
>> Совершенно верно. Вместо gen_tcp + http.
>>
>> Как я уже объяснил: парсинг HTTP запросов достаточно дорогая вещь,
>> поэтому имеет смысл убрать это из эрланга.
>> Текущая реализация в эрланге недостаточно хороша, видимо из-за того,
>> что гоняет хедеры отдельными сообщениями.
>
>

> IMHO опция packet={http|httph} с заголовками в отдельных сообщениях - это
> просто быстрый путь для писателей HTTP серверов. Заголовки можно разбирать и
> самостоятельно с помощью erlang:decode_packet/3.


>
> --
> Dmitry Vasiliev <dima at hlabs.org>
> http://hlabs.org
> http://twitter.com/hdima
>
>

Max Lapshin

unread,
Jan 4, 2012, 1:57:17 PM1/4/12
to erlang-...@googlegroups.com
2012/1/4 Dmitry Vasiliev <di...@hlabs.org>:

>
> IMHO опция packet={http|httph} с заголовками в отдельных сообщениях - это
> просто быстрый путь для писателей HTTP серверов. Заголовки можно разбирать и
> самостоятельно с помощью erlang:decode_packet/3.
>

Вы мне покажете сервер, использующий decode_packet и укладывающийся в
50% ядра при раздаче гигабита?

Sergey Prochorov

unread,
Jan 4, 2012, 6:15:40 PM1/4/12
to erlang-...@googlegroups.com
надо ток переименовать в microhttp, а то как-то не очевидно.

Max Lapshin

unread,
Jan 5, 2012, 3:13:03 AM1/5/12
to erlang-...@googlegroups.com
2012/1/5 Sergey Prochorov <seri...@gmail.com>:

> надо ток переименовать в microhttp, а то как-то не очевидно.
>

Это уже рабочие моменты.

Max Lapshin

unread,
Jan 5, 2012, 3:40:57 AM1/5/12
to erlang-...@googlegroups.com
Пока что результат не идеальный.

Судя по отчету httperf происходят серьезные залипания на приёме
соединений и на обработке.

> Connection time [ms]: connect 2552.1
> Reply time [ms]: response 49.8 transfer 8.5
> Net I/O: 1246639.1 KB/s (10212.5*10^6 bps)

Но при этом тесты http_bench (это специальная утилита, умеющая
скачивать HDS видеопоток) говорят, что при гигабитном потоке
CPU сервера колеблется в районе 20%.


Т.е. по CPU результат достигнут, а по плавности работы есть провал.

Массовый реконнект 20 тыс клиентов — рабочая ситуация, на которую надо
рассчитывать.


Ещё есть мысль спустить кеш прям в драйвер, что бы на самые горячие
запросы (а я из приложения хорошо знаю, какие они),
ответ происходил прям из уровня драйвера, без походов в бизнес-логику.

Max Lapshin

unread,
Jan 6, 2012, 4:39:03 AM1/6/12
to erlang-...@googlegroups.com
Результаты разработки:

* успешно запустилось вместе с cowboy:
https://github.com/erlyvideo/cowboy/commit/e617f740b7cf40732571171eaa750b9a6e792f48
* нормализация заголовков (content-length -> Content-Length) делается
прям в C, мутабельно
* преобразование заголовков в атомы так же, как в decode_packet
* сделан мультипроцессовый accept
* приделал поддержку file:sendfile

Результаты, конечно, не такие как у Льва с его косером, но уже ощутимо
ближе к varnish, чем у ковбоя/мисультина.

Основная разница в том, что парсинг HTTP осуществляется одним из самых
быстрых парсеров и результат шлется одним сообщением,
а не списком.


P.S. оказалось, что внутри эрланга есть куча разных имплементаций
хеш-таблиц, которые дублируются и пересекаются,
а чего-то единого нет.

Taras Halturin

unread,
Jan 6, 2012, 4:41:36 AM1/6/12
to erlang-...@googlegroups.com
Прям сводки с фронта :)

2012/1/6 Max Lapshin <max.l...@gmail.com>
--
Страница рассылки: http://groups.google.com/group/erlang-russian
Jabber-
конференция: erl...@conference.jabber.ru
Новости: http://erlanger.ru
Написать письмо: erlang-...@googlegroups.com
Отписаться: erlang-russia...@googlegroups.com

Lev Walkin

unread,
Jan 6, 2012, 5:20:22 PM1/6/12
to Erlang в России
On Jan 4, 1:37 am, Taras Halturin <haltu...@gmail.com> wrote:
> ставим перед ерланговским набором машин пару nginx'ов и размазываем
> нагрузку. Зачем втаскивать в аппликейшн функционал фронта? Нет, чисто

Вот у нас 25 лоад-балансеров стоит. Которые ведут трафик на 14
апликейшн-серверов (косер). Заметьте, не два nginx перед тучей
апликух, а 25 перед 14'ю. Потому что балансер менее эффективен, чем
само приложение.

--
vlm

Taras Halturin

unread,
Jan 6, 2012, 5:28:27 PM1/6/12
to erlang-...@googlegroups.com
Лев, боюсь Ваше приложение есть исключение. Контекст видать слишком большую рояль играет, потому и шаблон применения рвется ;). 

ЗЫж хочу попробовать прогнать явс с элементом смарт-балансинга на аппликейшн ноды (которые по мере нагрузки семафорят фронтовым явсам "погодь, я тут загибаюсь", чтобы те на некоторое время исключали из балансинга таких). ожидаю получить хороший резалт. как я уже где-то писал, явс с кернел пуллингом дает отличный прирост, близкий к нджинксу (тесты в гугле есть). как-то так. вообще, вся эта тема высоких нагрузок как правило имеет кучу своих подводных камней в каждом отдельно взятом случае. 

2012/1/7 Lev Walkin <lio...@gmail.com>
--
Страница рассылки: http://groups.google.com/group/erlang-russian
Jabber-
конференция: erl...@conference.jabber.ru
Новости: http://erlanger.ru
Написать письмо: erlang-...@googlegroups.com
Отписаться: erlang-russia...@googlegroups.com

JLarky

unread,
Jan 6, 2012, 5:47:53 PM1/6/12
to erlang-...@googlegroups.com
И да :) переименовать не только файл, но и модуль в
https://github.com/erlyvideo/gen_http/blob/master/README.md =)

2012/1/5 Sergey Prochorov <seri...@gmail.com>:


> надо ток переименовать в microhttp, а то как-то не очевидно.
>

Max Lapshin

unread,
Jan 6, 2012, 6:00:36 PM1/6/12
to erlang-...@googlegroups.com
2012/1/7 Lev Walkin <lio...@gmail.com>:

>
> Вот у нас 25 лоад-балансеров стоит. Которые ведут трафик на 14
> апликейшн-серверов (косер).
>

Лев. А зачем?

Т.е. два подвопроса:
1) не пробовали ли вы какой-нибудь чудо-балансировщик амазона?
2) почему не попросить косер балансировать? Там есть какая-то
сложность в балансировке?

Taras Halturin

unread,
Jan 6, 2012, 6:05:08 PM1/6/12
to erlang-...@googlegroups.com
есть предположение, что в его случае основная нагрузка - кол-во обслуживаемых коннектов, где давление нагрузки приходится именно на раздающие узлы, нежели на аппликухи, генерящие этот контент для получателей. кстати говоря, Максим, в Вашем случае с erlyvideo характер нагрузки будет схожий и придется ставить фронтов больше, чем кол-во аппликейшенов.

2012/1/7 Max Lapshin <max.l...@gmail.com>
--
Страница рассылки: http://groups.google.com/group/erlang-russian
Jabber-
конференция: erl...@conference.jabber.ru
Новости: http://erlanger.ru
Написать письмо: erlang-...@googlegroups.com
Отписаться: erlang-russia...@googlegroups.com

Max Lapshin

unread,
Jan 6, 2012, 6:14:40 PM1/6/12
to erlang-...@googlegroups.com
2012/1/7 JLarky <jla...@gmail.com>:

> И да :) переименовать не только файл, но и модуль в
> https://github.com/erlyvideo/gen_http/blob/master/README.md =)
>

Обновил.

И добавил режим http-клиента. Тоже очень неплохо вышло.

Dmitry Melnikov

unread,
Jan 7, 2012, 2:44:40 AM1/7/12
to erlang-...@googlegroups.com
Тарас,
было бы замечательно, если бы вы опубликовали результаты yaws + апликейшнс. Статью напишите, в конце концов.

2012/1/7 Taras Halturin <halt...@gmail.com>

Max Lapshin

unread,
Jan 8, 2012, 11:42:48 AM1/8/12
to erlang-...@googlegroups.com
Из gen_http уже получился неплохой клиент для HTTP.

Отличительные черты от других:

1) присылает одним сообщением весь распарсенный заголовок ответа
2) тело ответа так же присылает сообщениями, но не полутора
килобайтными, как {active,once}, а как попросишь.
Т.е. внутри драйвера буферизуется ответ и шлется дальше.

Простите, но zerocopy пока нет, для этого надо подпиливать
http_parser.c, что бы он не лез в тело сообщения.

На примитивной раздаче файлов (600 байт) в три раза медленнее nginx и
в три раза быстрее inets.

Max Lapshin

unread,
Jan 12, 2012, 6:28:53 PM1/12/12
to erlang-...@googlegroups.com
Доделана поддержка вебсокетов, как они выполнены в ковбое.

Конечно стоило бы воткнуть парсер их протокола, но фиг с ним.

Основные отличия от gen_tcp частично покрыты тестами.

Reply all
Reply to author
Forward
0 new messages