All streams
Search
Write a publication
Pull to refresh
299
0
Валентин Бартенев @VBart

Руководитель разработки «Angie Software»

Send message
Не могу поверить, что в самом популярном сервере поддержка пула потоков для длительных операций появилась только в 2015 году. При том, что паттерн Proactor известен науке по меньшей мере с 90-х.
Не очень понял, какая логическая связь между двумя процитированными предложениями. Мне кажется её нет, но отвечу. Не было необходимости, да и сейчас в большинстве случаев включать пул потоков нет необходимости. Для большинства типичных задач он не нужен. И даже в тех случаях, когда он нужен, есть и другие варианты решения проблемы.

Забавно, что автор винит ОС в том, что нет возможности узнать, какие данные закешированы, а какие нет.
Кешируйте сами — делов-то!
И файловую систему пишите сами, и tcp-стек, и т.д. по списку. Как только вы начинаете кэшировать данные сами, то натыкаетесь на необходимость копировать огромные объемы данных из ядра в пользовательское пространство и обратно. Такой системный вызов, как sendfile() был создан специально для того, чтобы этим не заниматься. Вы наверное не сталкивались, когда производительность начинает упираться в память и приходится делать всё возможное, чтобы уменьшать количество копирований.

Именно так работает, например, video download сервер известной социальной сети. То, что закешировано в памяти, он отдаёт сразу из потока-селектора. А за тем, чего в кеше нет, обращается асинхронно из отдельного пула. В результате один сервер отдаёт наружу до 40 Гбит/с, причём сам сервер написан даже не на C, а на «тормозной» Java.
Безусловно есть разные подходы к решению задачи. Каждый подход имеет свои плюсы и минусы. Инженеры из известной социальной сети выбрали такой подход и мы не знаем всех факторов, которые повлияли на их решение, поэтому я не возьмусь, например, давать оценки.

Информация про 40 Гбит/с особо не говорит ни о чем, ибо число сильно далеко от того, чтобы производить впечатление в 2015 году. И если уж мериться, то нужно делить этот показатель на стоимость сервера. Одно дело раздавать 40 Гбит/c с одного сервера, а другое дело раздавать треть всего интернет трафика в США и значительную долю всего мирового трафика с серверов, собранных из недорогих комплектующих. Когда количество серверов исчесляется десятками и сотнями тысяч, то их стоимость начинает иметь существенное значение. Ну и т. д. ;)
[удалено, промахнулся]
OSX не серверная система. Не стоит пытаться использовать её в этом качестве, можно наступить на огромное количество различных граблей.
Да, я пару месяцев назад общался с Милошем. С его патчами нам будет не хватать sendfile(). В этом отношении подход Мортона чуть более универсален, хотя менее эффективен и порождает маловероятный на практике, но всё же существующий race-condition между вызовом fincore() и соответвующим read() или sendfile(). С другой стороны fincore() нам не поможет при записи. При определенных условиях write() блокируется и хорошо бы об этом узнать заранее, чтобы отправить соответвующий таск в пул.

Милош настроен твердо: twitter.com/mtanski/status/611932316865298433
Почитал про overlapped. Кажется это скорее про реализацию нормального ядерного AIO и отдельные потоки там не нужны. Но я могу ошибаться, поскольку плохо знаком с windows даже на уровне пользователя.

Возможно sebres сможет ответить.

Важно понимать, что текущая реализация пулов не делает nginx многопоточным в плане обработки событий, он по-прежнему обрабатывает события на дескрипторах асинхронно и в одном потоке на рабочий процесс. А отдельные потоки из пулов используются исключительно для выгрузки долгих блокирующих операций, при этом они изолированы от основного потока через две сериализованные очереди (очередь ожидающих обработки заданий и очередь выполненных заданий).
Пожалуй к этому списку нечего больше добавить. Самый лучший nginx internals постигается чтением исходников.

Есть ещё выступление Валерия Холодкова на HL++ 2008 и статья по мотивам: www.grid.net.ru/nginx/nginx-modules.html и его же блог, посвященный исследованиям внутренностей nginx: www.nginxguts.com
Для интенсивного счета можно будет настроить отдельный пул потоков. NGINX позволяет создать их сколько угодно. Зачем смешивать все операции в одну кучу? У него будет своя очередь и необходимое количество тредов.
Наверное я тут перепутал с libaio. То POSIX AIO, что сейчас в glibc — это тоже обертка, но уже над тредами и довольно примитивная, чтобы её использовать для серьезных задач.
Так задача потока в пуле и заключается в том, чтобы ждать на блокирующей операции.
Нет. IOCP тут ортогонально. Прелесть данного механизма пулов потоков заключается в том, что он абсолютно независим от основного процесса обработки событий, не использует с ним разделяемых ресурсов, кроме двух очередей, что позволяет практически избежать локов и вмешательств в основные структуры.

Кроме того он реализован только под *nix.
Я об этом писал в статье. На линукс все плохо. См. lse.sourceforge.net/io/aio.html — с тех пор не продвинулись.
aio_*() просто обертка над всем этим.
Если вопрос ко мне, то не могу ничего порекомендовать, поскольку нет такого опыта, не пользовался. Вообще протокол в самом базовом виде достаточно простой и можно свое написать за день.

И мне кажется обработкой событий не fastcgi библиотека должна заниматься.
Так же, как это делает nginx, асинхронно обрабатывая сокеты в неблокирующемся режиме.
Чаще всего не используется. В Firefox, например, это нужно включать в about:config отдельно, см. network.http.pipelining.
На сервере ничего сложного в обработке таких запросов нет.
*CGI/HTTP должно хватить для большинства задач. Едва ли вы упретесь в протокол, зато получится гибко, стабильно и это будет на порядок легче поддерживать.

Планы были, но пока отсутствует понятный, формализованный, стабильный и безопасный API для сторонних модулей, любая такая документация будет неполноценной и быстро устаревать. На данный момент самая вменяемая документация — исходный код.
Она кусается не только для России. Многие клиенты с удовольствием отмечают, что NGINX Plus c поддержкой им обошелся буквально на порядок дешевле.
Собственно причем тут опенсорс. Не видел статистики, где бы утверждалось, что качество кода в закрытых проектах в среднем лучше. В данном случае у нас хотя бы есть возможность заглянуть внутрь и понять, что код говно.
Согласен. Столько далеко идущих выводов и вся аргументация строится на примере OpenSSL. Но нельзя это поделие рассматривать в качетсве примера, это просто адов ад, а не библиотека.
На всякий случай поясню, поскольку похоже есть еще люди, кто не в курсе дел.

Rambler отношения к NGINX не имеет. Игорь в Рамблере не работал программистом, а с 2011 и вовсе там не работает. NGINX это уже 4-ый год как NGINX, Inc.
Я теперь понял, что никакой сериализации сокетов больше нет, грубо говоря, epoll() и т.д. так же больше не нужны? Теперь ядро само пробуждает потоки по очереди?
Epoll никуда не делся, он всегда был отдельный в каждом рабочем процессе. Помимо принятия новых соединений, рабочие процессы делают много другой работы: читают запросы, отправляют ответы, обрабатывают таймауты, устанавливают соединения с бекендами. Рабочий процесс не может ждать на accept(), ему нужно работать с другими событиями, мониторить другие дескрипторы. Поэтому нужен механизм уведомления о событиях.

Как было:
все воркеры спят -> new socket data -> notification всех воркеров -> все воркеры вступают в борьбу за лок -> один захватывает лок и принимает коннект, остальные засыпают
Если интенсивность поступления новых соединения маленькая — то да. Для борьбы с этим эффектом как раз и существует accept_mutex, который отключает нотификацию у отдельных процессов в этом случае. Это можно видеть во втором бенчмарке, его включение снижает нагрузку на CPU.

Если нагрузка большая, как в первом бенчмарке в статье, то там всегда в общем-то есть новые соединения в очереди. И ситуаций когда рабочий процесс не получает соединения — практически не бывает. В этом случае accept_mutex скорее вредит, поэтому без него RPS получает немного выше. Тем не менее, лок в ядре никуда не девается, и когда несколько процессов одновременно зовут accept() на одном и том же сокете, то часть из них в итоге тратит на это больше времени, поскольку блокируются и вынуждены в ядре ждать своей очереди на получение лока.

Как стало:
воркеры спят в accept() -> new socket data -> ядро выбирает любой воркер, пробуждает и т.д.
См. выше. Воркерам не позволительно ждать на accept(), им нужно другие соединения обрабатывать.

Но то, что теперь будет уведомляться только один воркер, поскольку он мониторит только свои собственные дескрипторы, а не общие на все процессы — это верно. Но спать он по-прежнему будет в epoll_wait().

Тогда штука реально опасная, особенно учитывая политику распределения коннектов. Какой-нибудь залогиненный пользователь для которого страница генерируется гораздо дольше чем для анонимного всегда будет попадать на один и тот же воркер, забивая его.
Не настолько на самом деле. Псевдослучайное распределение будет раскидывать новые подключения от него случайным образом. С тем же успехом и раньше, установив keepalive соединение, можно было нагрузить запросами один рабочий процесс больше другого. Но в масштабах десятков и сотен тысяч соединений — все это довольно незначительные эффекты.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity