Лев Валкин рассказывал (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%. На основании этого можно сделать вывод о том, что действительно обработка самого протокола силами эрланга приводит к ужасной потере скорости и развивать данный кусок кода стоит.
> а может быть дешевле по трудозатратам просто распараллелить на несколько нод?
Слова «эрланг параллелится по нодам» многие люди воспринимают как волшебную мантру, которая вылечит от любой проблемы. Что вы собрались параллелить? Шторм из HTTP запросов?
ставим перед ерланговским набором машин пару nginx'ов и размазываем нагрузку. Зачем втаскивать в аппликейшн функционал фронта? Нет, чисто академически идея ясна, но с практической точки зрения, обычно проще и дешевле разделять зоны "ответственности", чем пытаться втиснуть в один котел и потом придаваться диву, какже быстро варится кашка в нем :). Главный вопрос (лично для меня) в таких случаях - а надо ли оно? ;)
PS: разумеется, нужно владеть контекстом, чтобы делать корректные выводы. сейчас же я меряю "общим знаменателем". возможно ваш случай именно тот, где нужно тащить обработку HTTP на более нижний уровень. Я вот тоже подумываю libgsoap затащить в драйвер... но это так, планы-планы, дюже время жалко да и нужда пока не сильно приспичила.
> 2012/1/4 Ghost <haltu...@gmail.com>: > > а может быть дешевле по трудозатратам просто распараллелить на несколько > нод?
> Слова <<эрланг параллелится по нодам>> многие люди воспринимают как > волшебную мантру, которая вылечит от любой проблемы. > Что вы собрались параллелить? Шторм из HTTP запросов?
> ставим перед ерланговским набором машин пару nginx'ов и размазываем нагрузку.
А потом, получив двухкратную нагрузку на CPU вдруг вспоминаем, что оказывается nginx не умеет склеивать два и более одновременных запросов к бекенду и не умеет кешировать контент в памяти.
Опачки, какая незадача! А ведь так всё в блогах красиво расписывают!
А заодно пишем долгие и подробные инструкции админам о том, как же помимо сервера ставить ещё и кеши к нему. И ловим кучу проблем с тем, что админы сделали что-то не так, а разгребать это приходится не им.
А что значит "склеивать"? На счет "не умеет кешировать" - неправда ваша, либо хотите от веб-сервера возможностей аппликейшн части. Последний абзац вообще не имеет ничего общего с технической частью - это проблемы коммуникации :).
> 2012/1/4 Taras Halturin <haltu...@gmail.com>: > > ставим перед ерланговским набором машин пару nginx'ов и размазываем > нагрузку.
> А потом, получив двухкратную нагрузку на CPU вдруг вспоминаем, что > оказывается nginx не умеет склеивать два и более одновременных > запросов к бекенду и не умеет кешировать контент в памяти.
> Опачки, какая незадача! А ведь так всё в блогах красиво расписывают!
> А заодно пишем долгие и подробные инструкции админам о том, как же > помимо сервера ставить ещё и кеши к нему. И ловим кучу проблем с тем, > что админы сделали что-то не так, а разгребать это приходится не им.
Меня немного смущает что https://github.com/erlyvideo/microtcp это tcp а не http, поясните бога ради в какой таки роли это надо использовать? Вместо gen_tcp или вместо чего-то вроде webmachine?
On Jan 4, 1:05 pm, Max Lapshin <max.laps...@gmail.com> wrote:
> Лев Валкин рассказывал (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%. На основании этого можно сделать > вывод о том, что действительно обработка самого протокола силами > эрланга приводит к ужасной потере скорости и развивать данный кусок > кода стоит.
> Меня немного смущает что https://github.com/erlyvideo/microtcp это tcp > а не http, поясните бога ради в какой таки роли это надо использовать? > Вместо gen_tcp или вместо чего-то вроде webmachine?
На сколько я понял код и Макса, то вместо gen_tcp с packet={http|httph}
> На сколько я понял код и Макса, то вместо gen_tcp с packet={http|httph}
Совершенно верно. Вместо gen_tcp + http.
Как я уже объяснил: парсинг HTTP запросов достаточно дорогая вещь, поэтому имеет смысл убрать это из эрланга. Текущая реализация в эрланге недостаточно хороша, видимо из-за того, что гоняет хедеры отдельными сообщениями.
>> На сколько я понял код и Макса, то вместо gen_tcp с packet={http|httph}
> Совершенно верно. Вместо gen_tcp + http.
> Как я уже объяснил: парсинг HTTP запросов достаточно дорогая вещь, > поэтому имеет смысл убрать это из эрланга. > Текущая реализация в эрланге недостаточно хороша, видимо из-за того, > что гоняет хедеры отдельными сообщениями.
Дело в том, что http_parser.c берет на себя половину HTTP, в частности такие штуки как keepalive и т.п. и работает сильно удобнее в том плане, что сразу присылает все заголовки, не надо накапливать и возиться с этим.
Но кое чего из cowboy_http_request хочется повторно использовать и тут возникают некоторые вопросы.
> 2012/1/4 Yurii Rashkovskii <yra...@gmail.com>: >> Насколько я понимаю, эта штука должна быть весьма вкручиваема в >> cowboy, благодаря его модульности.
> Дело в том, что http_parser.c берет на себя половину HTTP, в частности > такие штуки как keepalive и т.п. и работает > сильно удобнее в том плане, что сразу присылает все заголовки, не надо > накапливать и возиться с этим.
> Но кое чего из cowboy_http_request хочется повторно использовать и тут > возникают некоторые вопросы.
>>> На сколько я понял код и Макса, то вместо gen_tcp с packet={http|httph}
>> Совершенно верно. Вместо gen_tcp + http.
>> Как я уже объяснил: парсинг HTTP запросов достаточно дорогая вещь, >> поэтому имеет смысл убрать это из эрланга. >> Текущая реализация в эрланге недостаточно хороша, видимо из-за того, >> что гоняет хедеры отдельными сообщениями.
> IMHO опция packet={http|httph} с заголовками в отдельных сообщениях - это > просто быстрый путь для писателей HTTP серверов. Заголовки можно разбирать и > самостоятельно с помощью erlang:decode_packet/3.
> IMHO опция packet={http|httph} с заголовками в отдельных сообщениях - это > просто быстрый путь для писателей HTTP серверов. Заголовки можно разбирать и > самостоятельно с помощью erlang:decode_packet/3.
Вы мне покажете сервер, использующий decode_packet и укладывающийся в 50% ядра при раздаче гигабита?
Судя по отчету 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 тыс клиентов — рабочая ситуация, на которую надо рассчитывать.
Ещё есть мысль спустить кеш прям в драйвер, что бы на самые горячие запросы (а я из приложения хорошо знаю, какие они), ответ происходил прям из уровня драйвера, без походов в бизнес-логику.
* успешно запустилось вместе с cowboy: https://github.com/erlyvideo/cowboy/commit/e617f740b7cf40732571171eaa... * нормализация заголовков (content-length -> Content-Length) делается прям в C, мутабельно * преобразование заголовков в атомы так же, как в decode_packet * сделан мультипроцессовый accept * приделал поддержку file:sendfile
Результаты, конечно, не такие как у Льва с его косером, но уже ощутимо ближе к varnish, чем у ковбоя/мисультина.
Основная разница в том, что парсинг HTTP осуществляется одним из самых быстрых парсеров и результат шлется одним сообщением, а не списком.
P.S. оказалось, что внутри эрланга есть куча разных имплементаций хеш-таблиц, которые дублируются и пересекаются, а чего-то единого нет.
> https://github.com/erlyvideo/cowboy/commit/e617f740b7cf40732571171eaa... > * нормализация заголовков (content-length -> Content-Length) делается > прям в C, мутабельно > * преобразование заголовков в атомы так же, как в decode_packet > * сделан мультипроцессовый accept > * приделал поддержку file:sendfile
> Результаты, конечно, не такие как у Льва с его косером, но уже ощутимо > ближе к varnish, чем у ковбоя/мисультина.
> Основная разница в том, что парсинг HTTP осуществляется одним из самых > быстрых парсеров и результат шлется одним сообщением, > а не списком.
> P.S. оказалось, что внутри эрланга есть куча разных имплементаций > хеш-таблиц, которые дублируются и пересекаются, > а чего-то единого нет.
On Jan 4, 1:37 am, Taras Halturin <haltu...@gmail.com> wrote:
> ставим перед ерланговским набором машин пару nginx'ов и размазываем > нагрузку. Зачем втаскивать в аппликейшн функционал фронта? Нет, чисто
Вот у нас 25 лоад-балансеров стоит. Которые ведут трафик на 14 апликейшн-серверов (косер). Заметьте, не два nginx перед тучей апликух, а 25 перед 14'ю. Потому что балансер менее эффективен, чем само приложение.
Лев, боюсь Ваше приложение есть исключение. Контекст видать слишком большую рояль играет, потому и шаблон применения рвется ;).
ЗЫж хочу попробовать прогнать явс с элементом смарт-балансинга на аппликейшн ноды (которые по мере нагрузки семафорят фронтовым явсам "погодь, я тут загибаюсь", чтобы те на некоторое время исключали из балансинга таких). ожидаю получить хороший резалт. как я уже где-то писал, явс с кернел пуллингом дает отличный прирост, близкий к нджинксу (тесты в гугле есть). как-то так. вообще, вся эта тема высоких нагрузок как правило имеет кучу своих подводных камней в каждом отдельно взятом случае.
> On Jan 4, 1:37 am, Taras Halturin <haltu...@gmail.com> wrote: > > ставим перед ерланговским набором машин пару nginx'ов и размазываем > > нагрузку. Зачем втаскивать в аппликейшн функционал фронта? Нет, чисто
> Вот у нас 25 лоад-балансеров стоит. Которые ведут трафик на 14 > апликейшн-серверов (косер). Заметьте, не два nginx перед тучей > апликух, а 25 перед 14'ю. Потому что балансер менее эффективен, чем > само приложение.