Почему Windows. Все просто, в корпоративном секторе Windows на сервере, да и на рабочих станциях — нередко обязательная программа. И от этих требований к платформе, например в ультимативной форме озвученных клиентом, никуда не денешься.
И раз уж имеем Windows, но не хочется мучиться с IIS, apache и иже с ними, если хочется использовать любимые инструменты, а nginx однозначно к ним относится, то приходится иногда мириться даже с некоторыми ограничениями на этой платформе. Вернее приходилось…
Хотя нужно заметить, что даже с этими ограничениями, nginx даст фору практически любому веб-серверу под windows по многим факторам, в том числе по стабильности, потреблению памяти, а главное производительности.
Спешу сразу поделится хорошей новостью — больше ограничений, критичных к высокой производительности, при использовании nginx под windows практически не существует, и последнее из критичных, с высокой долей вероятности, тоже скоро отпадет. Но по порядку…
Здесь описаны известные проблемы nginx 4 windows, а именно:
- Рабочий процесс может обслуживать не более 1024 одновременных соединений.
- Кэш и другие модули, требующие поддержки разделяемой памяти, не работают под Windows Vista и более поздними версиями в связи с тем, что на этих версиях Windows включена рандомизация адресного пространства.
- Хоть и возможен запуск нескольких рабочих процессов, только один из них реально работает.
Я немного изменил порядок, т.к. именно в таком порядке я разбирался с этими ограничениями, так сказать отсортировано «исторически».
1024 одновременных соединений
На самом деле это не правда, вернее не совсем правда — nginx с незапамятных времен можно было собрать под Windows без этого ограничения — нужно было просто на этапе сборки определить
FD_SETSIZE
равным нужному вам количеству соединений.Например для VS добавив директиву
--with-cc-opt=-DFD_SETSIZE=10240
, воркер nginx сможет управляться с 10K одновременными соединениями, если в конфигурации вы укажете worker_connections 10240;
.
Кэш и другие модули, требующие поддержки разделяемой памяти
Все эти функции и модули до недавнего времени действительно не работали под Windows, начиная с версии x64 или там где по умолчанию вся система работает с включенным ASLR.
Причем отключение ASLR для nginx ничего не меняет, т.к. функции для работы с shared memory зашиты глубоко в kernel, т.е. ASLR (а с ним вероятно и DEP, с ним почему-то не получалось) нужно отключать для всей системы.
Это на самом деле довольно не маленький список функционала: Кэш, любые зоны, соответственно и limit_req и т.д. и т.п. Кстати без поддержки разделяемой памяти гораздо труднее было бы убрать 3-е ограничение, т.е. реализовать поддержку multiple workers под windows.
Не буду утомлять читателя как я с этим боролся, но совместно с Максом (спасибо @mdounin) мы таки допилили это до релизной версии. Немного об этом, кому интересно см. под спойлером или в исходниках hg.nginx.org или github…
Соответственно без того чтобы переписать весь функционал, работающий с pointer внутри shared mem в nginx — есть единственный вариант,
Начало дискуссии про это можно почитать здесь. Максим, кстати починил упущенную мной проблему (remapping), иногда возникающую после перезагрузки воркеров (reload налету).
Viva open source!
Т.е. официально это ограничение больше не действует с Release 1.9.0 от 28 Apr 2015:
Feature: shared memory can now be used on Windows versions with
address space layout randomization.
Только один рабочий процесс реально работает.
В nginx есть процесс мастер и дочерние процессы, называемые рабочими или worker.
Под windows у nginx может быть запущено несколько рабочих процессов, т.е. указав "
worker_processes 4;
" в конфигурации, вы заставите мастера запустить 4-е дочерних рабочих процесса. Проблема состоит в том, что только один из них, «украв» listener-соединение у мастера (используя SO_REUSEADDR) будет действительно слушать этот сокет, т.е. делать accept входящих соединений. В результате же у других воркеров — нет входящих соединений — нет работы.Это ограничение связано с технической реализацией winsock, и единственный способ получить распределенное listener-соединение для всех рабочих процессов в Windows — это клонировать сокет из мастер-процесса, т.е. использовать inherited handle от сокета из него.
Кому интересны подробности реализации, могут посмотреть их под спойлером или в исходниках, пока только у меня на гитхаб.
CreateProcess
), используя bInheritHandle=TRUE
, и установив SECURITY_ATTRIBUTES::bInheritHandle
при создании сокета тоже равным TRUE, скорее всего у вас ничего не выйдет, т.е. в рабочем процессе используя этот handle, вы получите «failed (10022: An invalid argument was supplied)». А «успешно» продублировав этот сокет с помощью DuplicateHandle
, дублированный handle тоже не примет ни одна функция работающая с сокетами (вероятно с ошибкой 10038 — WSAENOTSOCK).Почему так происходит приоткрывает одна цитата из MSDN — DuplicateHandle:
Проблема в том, что для дублирования handle с помощью WSADuplicateSocket, необходимо заранее знать pid процесса, т.е. это нельзя сделать до того как процесс был бы запущен.Sockets. No error is returned, but the duplicate handle may not be recognized by Winsock at the target process. Also, using DuplicateHandle interferes with internal reference counting on the underlying object. To duplicate a socket handle, use the WSADuplicateSocket function.
В результате, чтобы сообщить дочернему процессу информацию полученную мастером от WSADuplicateSocket, необходимую для создания сокета-клона, в рабочем процессе — имеем два варианта, либо использовать что-нибудь вида IPC, например как это описано в MSDN — WSADuplicateSocket, либо передать это через shared memory (благо мы уже это выше починили).
Я решил использовать второй вариант, т.к. считаю, что это наименее трудоемкий из двух и наиболее быстрый способ реализации наследования соединения.
Ниже изменения в алгоритме запуска рабочих процессов под windows (помечены
*
):- Мастер-процесс создает все listener-сокеты;
- [cycle] Мастер-процесс создает рабочий процесс;
*
[win32] мастер вызывает новую функциюngx_share_listening_sockets
: для каждого listener-сокет опрашивается информация (для наследования) конкретно для этого нового воркера, («клонированная» через WSADuplicateSocket для pid), которая будет сохранена в shared memory — shinfo (protocol structure);- Мастер-процесс ждет пока worker установит событие о готовности — event «worker_nnn»;
*
[win32] Рабочий процесс выполняет новую функциюngx_get_listening_share_info
для получения информации наследования shinfo, которая будет использоваться для создания нового дескриптора сокета для shared listener-сокета мастер-процесса;*
[win32] Рабочий процесс создает все listener-сокеты, используя информацию shinfo от мастер-процесса;- Рабочий процесс устанавливает событие — event «worker_nnn»;
- Мастер-процесс прекращает ожидание, и создает следующий рабочий процесс, повторяя [cycle].
Если нужно, здесь ссылка на дискуссию о фиксе, дабы она будет.
В результате, nginx под windows запускает теперь N «полноценных», с точки зрения «прослушивания», а главное установления соединения, рабочих процессов, которые обрабатывают входящие соединения действительно параллельно.
Этот фикс правда еще лежит «пул-реквестом» (я отправил changeset в nginx-dev), но его уже можно попробовать например скачав с моего github и самостоятельно собрав под windows. Если будут желающие выложу куда-нибудь бинарник.
Я довольно долго истязал свое железо, гоняя это тестами и под нагрузочными «скриптами» — результат, все воркеры нагружены более-менее равномерно и действительно работают параллельно. Также я пробовал на лету (reload-ом) перезагружать nginx и случайным образом «убивал» некоторых воркеров имитируя «crash» последних — все работает без малейшего нарекания.
Пока проявился единственный «недостаток», имхо — если запустить
netstat /abo | grep LISTEN
то вы увидите только мастер-процесс в списке «слушателей», хотя в реальности как раз он никогда не устанавливает соединение, только его дочерние рабочие процессы.
Кстати, мой опыт пока говорит, что accept_mutex
для windows-платформы вероятно нужно отключать "accept_mutex off;
", т.к. по крайней мере на моих тестовых системах, с включенным accept_mutex
они работали ощутимо медленнее, чем с выключенным. Но это думаю каждый должен проверять экспериментально (т.к. зависит от кучи параметров, типа количество ядер, воркеров, keep-alive соединений и т.д. и т.п.).
Ну и как без красивых табличек с числами сравнения производительности, до (первый столбец помечен **NF) и после.
Тест сделан на Windows7 — i5-2400 cpu @ 3.10GHz (4 core).
Request: статика, 452 байта (+ header) — маленькие gif-иконки.
Workers x Concur. | 1 x 5 **NF | 2 x 5 | 4 x 5 | 4 x 15 |
---|---|---|---|---|
Transactions | 5624 hits | 11048 hits | 16319 hits | 16732 hits |
Availability | 100.00 % | 100.00 % | 100.00 % | 100.00 % |
Elapsed time | 2.97 secs | 2.97 secs | 2.97 secs | 2.96 secs |
Data transferred | 2.42 MB | 4.76 MB | 7.03 MB | 7.21 MB |
Response time | 0.00 secs | 0.00 secs | 0.00 secs | 0.00 secs |
Transaction rate | 1893.60 trans/sec | 3719.87 trans/sec | 5496.46 trans/sec | 5645.07 trans/sec |
Throughput | 0.82 MB/sec | 1.60 MB/sec | 2.37 MB/sec | 2.43 MB/sec |
Concurrency | 4.99 | 4.99 | 4.99 | 14.92 |
Successful transactions | 5624 | 11048 | 16319 | 16732 |
Failed transactions | 0 | 0 | 0 | 0 |
Longest transaction | 0.11 | 0.11 | 0.11 | 0.11 |
Shortest transaction | 0.00 | 0.00 | 0.00 | 0.00 |
И да прибудет с вами nginx и под windows.
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий