Комментарии 24
К сожалению, из-за форматирования, т.е. отсутствия оного, статья нечитабельная :(
rwasa повеселее для подобных задач будет.
Я – разработчик nxweb. Данная статья была написана в декабре 2011 года, но почему-то только сейчас вышла из песочницы. Через три с половиной года. Попытаюсь немного освежить информацию.
За прошедшее время nxweb оброс немалым функционалом:
При этом он достаточно стабилен, давно использую его как фронтенд. На некоторых сайтах работает год и больше без перезапуска.
Кстати, вот официальный сайт: nxweb.org. Работает на nxweb + своя CMS на Python. Там, правда, в русской версии практически ничего не написано, но в английской есть несколько статей для быстрого старта.
За прошедшее время nxweb оброс немалым функционалом:
- http-proxy (удобно для Java)
- встроенный Python
- шаблоны страниц с наследованием блоков
- SSL
- файловый кеш ответов бекенда
- кешируемое gzip-сжатие
- масштабирование изображений
- конфиг-файл в формате json
- подгружаемые модули
- access log
- ...
При этом он достаточно стабилен, давно использую его как фронтенд. На некоторых сайтах работает год и больше без перезапуска.
Кстати, вот официальный сайт: nxweb.org. Работает на nxweb + своя CMS на Python. Там, правда, в русской версии практически ничего не написано, но в английской есть несколько статей для быстрого старта.
Из обработчика, работающего в отдельном потоке (worker thread), в т.ч. Python, это пока не реализовано. Worker должен вернуть серверу весь ответ целиком.
Для асинхронных модулей на C такого ограничения нет, но написание асинхронных модулей – задача нетривиальная. Нужно интегрироваться в event loop сервера. В т.ч. нужно асинхронно мониторить источник информации, сигнализирующий о необходимости отправки клиентам новых пакетов данных.
Для асинхронных модулей на C такого ограничения нет, но написание асинхронных модулей – задача нетривиальная. Нужно интегрироваться в event loop сервера. В т.ч. нужно асинхронно мониторить источник информации, сигнализирующий о необходимости отправки клиентам новых пакетов данных.
Встроенный python. Интересно. Означает ли это, что nxweb может запускать django приложения? Если да, то через какой handler?
Не может быть. Как проверяли?
ab -n 100 -k -c 10 nxweb.org
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, www.zeustech.net
Licensed to The Apache Software Foundation, www.apache.org
Benchmarking nxweb.org (be patient)...apr_pollset_poll: The timeout specified has expired (70007)
Total of 10 requests completed
если вырубить, не дожидаясь таймаута:
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, www.zeustech.net
Licensed to The Apache Software Foundation, www.apache.org
Benchmarking nxweb.org (be patient)...apr_pollset_poll: The timeout specified has expired (70007)
Total of 10 requests completed
если вырубить, не дожидаясь таймаута:
ab -n 100 -k -c 10 nxweb.org
ab -n 100 -k -c 10 nxweb.org
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, www.zeustech.net
Licensed to The Apache Software Foundation, www.apache.org
Benchmarking nxweb.org (be patient)...^C
Server Software: nxweb/3.3.0-dev
Server Hostname: nxweb.org
Server Port: 80
Document Path: /
Document Length: 1199 bytes
Concurrency Level: 10
Time taken for tests: 3.558 seconds
Complete requests: 10
Failed requests: 2
(Connect: 0, Receive: 0, Length: 2, Exceptions: 0)
Non-2xx responses: 10
Keep-Alive requests: 10
Total transferred: 236160 bytes
HTML transferred: 231922 bytes
Requests per second: 2.81 [#/sec] (mean)
Time per request: 3557.915 [ms] (mean)
Time per request: 355.791 [ms] (mean, across all concurrent requests)
Transfer rate: 64.82 [Kbytes/sec] received
Connection Times (ms)
min mean[±sd] median max
Connect: 3 3 0.2 3 4
Processing: 14 39 22.7 58 64
Waiting: 14 39 22.7 58 64
Total: 17 42 22.7 62 67
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, www.zeustech.net
Licensed to The Apache Software Foundation, www.apache.org
Benchmarking nxweb.org (be patient)...^C
Server Software: nxweb/3.3.0-dev
Server Hostname: nxweb.org
Server Port: 80
Document Path: /
Document Length: 1199 bytes
Concurrency Level: 10
Time taken for tests: 3.558 seconds
Complete requests: 10
Failed requests: 2
(Connect: 0, Receive: 0, Length: 2, Exceptions: 0)
Non-2xx responses: 10
Keep-Alive requests: 10
Total transferred: 236160 bytes
HTML transferred: 231922 bytes
Requests per second: 2.81 [#/sec] (mean)
Time per request: 3557.915 [ms] (mean)
Time per request: 355.791 [ms] (mean, across all concurrent requests)
Transfer rate: 64.82 [Kbytes/sec] received
Connection Times (ms)
min mean[±sd] median max
Connect: 3 3 0.2 3 4
Processing: 14 39 22.7 58 64
Waiting: 14 39 22.7 58 64
Total: 17 42 22.7 62 67
ApacheBench стар как мир, он использует HTTP 1.0 и, похоже, не понимает chunked-encoding.
Строго говоря, nxweb не должен был бы использовать chunked encoding для клиента, обращающегося по HTTP 1.0, так как chunked encoding – это особенность HTTP 1.1. Пожалуй, это недочет. Не уверен, правда, что мне хочется его исправлять. Ведь живые клиенты, использующие HTTP 1.0, сейчас уже практически не встречаются.
Хотя, я еще подумаю над этим.
nxweb (да и другие серверы) использует chunked encoding не всегда, а только в случаях, когда размер ответа не известен заранее. Обычно это означает, что контент формируется на лету с помощью SSI или шаблонов.
Попробуйте вот так:
Строго говоря, nxweb не должен был бы использовать chunked encoding для клиента, обращающегося по HTTP 1.0, так как chunked encoding – это особенность HTTP 1.1. Пожалуй, это недочет. Не уверен, правда, что мне хочется его исправлять. Ведь живые клиенты, использующие HTTP 1.0, сейчас уже практически не встречаются.
Хотя, я еще подумаю над этим.
nxweb (да и другие серверы) использует chunked encoding не всегда, а только в случаях, когда размер ответа не известен заранее. Обычно это означает, что контент формируется на лету с помощью SSI или шаблонов.
Попробуйте вот так:
ab -n 100 -c 10 -k http://nxweb.org/i/images/wait.gif
Принцип работы веб-сервера прост, и реализовать его несложно. Есть работающие примеры буквально из 100 строк кода. Но, как хорошо видно из Вашего поста, нюансов там несметное множество. А если захочется сделать на собственном сервере полноценный веб-сайт (со статикой, бекендом, SSI, gzip-компрессией, кешированием, ...) то придется много еще чего доделать. Вот так и я, начал с самого простого, в итоге за несколько лет пришел к тому, что есть сейчас.
О безопасности история умалчивает.
Безопасность – штука многогранная…
Сайты, работающие на nxweb, уже не раз бывали под DDoS'ом – успешно его отбивали (хотя DDoS тоже бывает разный), в т.ч. благодаря кешированию. Не знаю, пытались ли их взломать, но и не слышал о том, чтобы это кому-то удалось.
Разумеется, я думал о безопасности, когда писал код, но уязвимости можно найти всегда в любом коде. Обязательно их устраню, когда обнаружатся.
Сайты, работающие на nxweb, уже не раз бывали под DDoS'ом – успешно его отбивали (хотя DDoS тоже бывает разный), в т.ч. благодаря кешированию. Не знаю, пытались ли их взломать, но и не слышал о том, чтобы это кому-то удалось.
Разумеется, я думал о безопасности, когда писал код, но уязвимости можно найти всегда в любом коде. Обязательно их устраню, когда обнаружатся.
> Те превратить kqueue() в epoll() может любой дурак, тут ума совсем не надо.
В современном ядре Linux epoll обладает практически всеми фичами kqueue (за исключением, пожалуй EVFILT_PROC). Конечно, иногда это происходит ценой написания большего количества кода, но результат получается весьма достойным.
Кстати, использование timerfd для таймеров говорит о том, что не каждый дурак способен способен реализовать таймеры эффективно: в реальных приложениях, как правило, нужны таймеры, срабатывающие через равные промежутки времени, что можно сделать за O(1) в отличие от бездумного использования heap'ов для всего и вся.
>но вот отдачи много: 2-16 мегабит в одно соединение. И куча таких вот соединение, «проксирование» IPTV потоков один-к-многим
Я абсолютно не сведущ в FreeBSD-специфичных API, но как раз для этого в Linux завезли: a) splice (-30% cpu load при проксировании «один к одному») б) vmsplice (для отдачи кучи мегабит из одного соединения, для мультиплексирования) в) tee (для мультиплексирования «с трюкачествами»)
В современном ядре Linux epoll обладает практически всеми фичами kqueue (за исключением, пожалуй EVFILT_PROC). Конечно, иногда это происходит ценой написания большего количества кода, но результат получается весьма достойным.
Кстати, использование timerfd для таймеров говорит о том, что не каждый дурак способен способен реализовать таймеры эффективно: в реальных приложениях, как правило, нужны таймеры, срабатывающие через равные промежутки времени, что можно сделать за O(1) в отличие от бездумного использования heap'ов для всего и вся.
>но вот отдачи много: 2-16 мегабит в одно соединение. И куча таких вот соединение, «проксирование» IPTV потоков один-к-многим
Я абсолютно не сведущ в FreeBSD-специфичных API, но как раз для этого в Linux завезли: a) splice (-30% cpu load при проксировании «один к одному») б) vmsplice (для отдачи кучи мегабит из одного соединения, для мультиплексирования) в) tee (для мультиплексирования «с трюкачествами»)
>Про timerfd и heap~ы не понял, можно развернуть мысль?
Как правило, все таймеры можно объединить в heap (по-русски это вроде «пирамидой» называют). Однако, если таймерами управляет приложение, оно может объединить равноинтервальные таймеры в связанный список, привязанный к единственному элементу heap-а. В итоге rearm таймера будет иметь сложность O(1) вместо логарифмических вставки/удаления в heap. Именно поэтому я весьма скептически отношусь к помещению десятков тысяч таймеров в kqueue.
>Меня вот напрягло что запись+чтение могут придти разом, и udata для них никак не разделяется, этот код я так и не отладил, оставив рабочим вариантом когда один описатель — один тип событий.
Хранить в udata файловый дескриптор, а в userspace к файловому дескриптору привешивать цепочки watcher'ов, как это сделано в libev
>Кстати, sendfile() в линухе тоже весь такой обрезанный: не умеет данные до/после файла из буферов посылать
Но зачем? TCP_CORK + send/writev. Зачем плодить лишние сущности которые делают то же, что уже существующие? Цена syscall-а не такая высокая, как это многим кажется.
Как правило, все таймеры можно объединить в heap (по-русски это вроде «пирамидой» называют). Однако, если таймерами управляет приложение, оно может объединить равноинтервальные таймеры в связанный список, привязанный к единственному элементу heap-а. В итоге rearm таймера будет иметь сложность O(1) вместо логарифмических вставки/удаления в heap. Именно поэтому я весьма скептически отношусь к помещению десятков тысяч таймеров в kqueue.
>Меня вот напрягло что запись+чтение могут придти разом, и udata для них никак не разделяется, этот код я так и не отладил, оставив рабочим вариантом когда один описатель — один тип событий.
Хранить в udata файловый дескриптор, а в userspace к файловому дескриптору привешивать цепочки watcher'ов, как это сделано в libev
>Кстати, sendfile() в линухе тоже весь такой обрезанный: не умеет данные до/после файла из буферов посылать
Но зачем? TCP_CORK + send/writev. Зачем плодить лишние сущности которые делают то же, что уже существующие? Цена syscall-а не такая высокая, как это многим кажется.
Не думали написать полноценную статью на эту тему? Похоже вы прилично успели в этом покапаться.
Мало кто копал в этой теме, инфа скудная. Каких-то сравнительных тестов или анализа разных подходов, как по типам архитектур серверов, так и по используемым библиотекам и системным возможностям — просто нет.
- epoll/kqueue — возможности, особенности, etc
- libev, libevent, libuv — сравнение, производительность, etc
- в целом разработка хайлоад сетевого сервиса: архитектуры, «подводные камни», etc
Мало кто копал в этой теме, инфа скудная. Каких-то сравнительных тестов или анализа разных подходов, как по типам архитектур серверов, так и по используемым библиотекам и системным возможностям — просто нет.
спасибо за статью, она подвигла меня задуматься об оптимизации своего кода
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
nxweb – HTTP сервер для приложений на Си