Pull to refresh
283
0
Валентин Бартенев @VBart

Руководитель разработки ООО «Веб-Сервер»

Send message

"usable" не означает, что это правильно и удобно. Всё-таки писать на С++ в стиле С - довольно неблагодарное занятие, а если не в стиле С, то это означает сильно отличаться от кода стандартных библиотек Python, что также сомнительный выбор для задачи расширения возможностей Python (как и любой необоснованный зоопарк языков в проекте).

Исключение тут составляют библиотеки, которые изначально были написаны на другом языке, а далее уже был сделан биндинг для Python.

Вообще-то Numpy написан с использованием С, а не на С++. Большинство подобных библиотек написано на С, что логично, т.к. сам Python тоже написан на С.

Головной процесс работает от root и не принимает клиентских соединений, как по соображениям безопасности, так и стабильности (его падение будет фатальным).

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

Нет, на первый взгляд это совсем не то. В nginx есть разделяемая память и структуры данных, которые могут в ней жить. Данный модуль похоже просто реализует интерфейс к такой структуре типа очереди в разделяемой памяти.

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

У нас же задача значительно сложнее:
1. Каждый рабочий процесс, в который может потенциально прийти запрос статуса, должен знать о существовании других процессов.
2. Поскольку сами процессы их число - величина не постоянная, то в момент, когда какой-то из процессов завершается или стартует новый, другие рабочие процессы должны узнавать об этом и получать каналы связи с новыми процессами.
3. Через эти каналы связи процесс, в который придет запрос статуса должен будет запросить из других процессов информацию, разбудив их и дождавшись, когда информация поступит из каждого процесса.
4. Все это сопровождается обработкой всевозможных race conditions, например, когда мы направили запрос в процесс, который как раз решил завершиться в этот момент, но он ещё не получил запроса, а мы ещё не получили информации о том, что он завершается.
5. Сама разделяемая память разумеется может служить таким каналом связи, но будет необходим ещё механизм, который позволит будить процессы, уведомлять их о новой информации или запросе из другого процесса.

Собственно в Unit-е мы это сделали.

Сдерживающие факторы такие, что это другой продукт получается, который не будет совместим со всеми сторонними модулями, а также потребует переписывания заметного количества кода существующих модулей. Дело в том, что весь текущий код nginx и сторонний код написан в парадигме отсутствия необходимости обеспечения thread-safety.

Да, было бы здорово такое реализовать. Но в nginx это достаточно нетривиальная задача. Можно получить информацию о запросах, которые обрабатываются в том же процессе, куда поступил запрос к статистике, но для извлечения информации из других рабочих процессов потребуется некий механизм ipc, который на данный момент в nginx просто отсутствует. Думаю рано или поздно мы это реализуем.

Спасибо за напоминание. Действительно, с кросс-компиляцией беда. Занес ваше пожелание в список потенциальных улучшений.

Вы же не считаете, что отказавшись помогать пользователю выполнить законодательное требование - кто-то бы улучшил репутацию того самого органа или хоть как-то поборол тех, кто её испортил?

На ваш пример у меня есть свой пример. Сюжет тот же, но только помочь надо остановить насильника или террориста. К чему эти фантазии?

Очевидно, что преступники и террористы пользуются шифрованными каналами связи для подготовки своих преступлений. В то же время бывают и обратные ситуации, когда злоупотребляют полномочиями не для тех целей, которым по идее должны служить. Мир не состоит из черного и белого.

Прекрасная подмена:
один разработчик -> разработчики, они
однажды ответил на вопросы пользователя из списка рассылки сообщества -> охотно помогали.

Процитирую начало сообщения, на которое вы ссылаетесь:
> Мы состоим в реестре организаторов распространения информации и поэтому обязаны предоставлять в надзорный орган ключи tls сессий.

Видимо если бы бывший коллега не подсказал, то озвученная обязанность пропала или автор не продолжил бы использовать Apache, с которым у него уже все работало без проблем. К разработчикам Apache тоже вопросы?

Без ключа сертификат в принципе невозможно было сгенерировать. Подозреваю, что либо вы его сами загружали в процессе заказа и не обратили на это внимание, либо reg.ru где-то в процессе заказа сгенерировал ключ для вас и просил его сохранить.

Вас смущает, что Москва следует мировым тенденциям в том, что таких заводов в черте города быть не должно?

Если они оказались в одном процессе, это значит, что соединение 2 пришло после того, как соединение 1 обработалось. Т.е. что в одном процессе, что в разных никакой параллельности не будет, так как сами соединения приходят последовательно, а не одновременно — параллельно обрабатывать просто нечего.

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

Более того. Ядро одновременно сообщает о множестве событий. Т.е. одним вызовом рабочий процесс может получить сразу сотни новых событий.

Так работают асинхронные сервера. Один рабочий процесс nginx может обрабатывать события на миллионах соединений.

Вот если бы все 12 соединений пришли одновременно и все почему-то оказались в первом процессе… но с чего бы? Первый процесс заберет первое соединение и пойдет его обрабатывать, второе заберет второй и так далее.

Первый процесс может забрать все 12 соединений. Обработка соединения сводится к вызову accept(). Я даже не уверен, что ядро вообще разбудит другие процессы, если все 12 соединений были им приняты за один такт обработки сетевого трафика. Но это не точно.

В любом случае, любая приоритезация одних рабочих процессов над другими - создает перекос. Как я уже упомянул выше, чаще всего рабочие процессы заняты ожиданием новых событий.

А нынче с HTTP/2 и веб-сокетами у нас часто преобладают долгоживущие соединения. Поэтому событие установки нового соединение - это одно сравнительно редкое событие. Зато каждое такое соединение затем порождает тысячи событий в процессе его обслуживания.

Один рабочий процесс имеет один поток. Поэтому в один момент времени он может из очереди брать и обрабатывать только одно событие. А сколько событий будет в очереди - зависит от того, сколько вернуло их ядро в одном вызове epoll_wait(). По умолчанию один вызов может вернуть до 512 событий.

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

Скорее всего не ляжет, т.к. исходящий порт для каждого соединения будет выбираться разный. Иначе как идентифицировать какие TCP пакеты какому соединению принадлежат?

Речь не идет, когда сервер максимально загружен и работает на пределе своих возможностей (чаще всего это не так и какое-то количество ресурсов остается свободно). Вы рассматриваете процесс обработки нагрузки бинарно: справился или не справился, успел - не успел. Но все гораздо сложнее.

Попробую объяснить максимально просто. Представьте, что у вас есть 12 соединений, на которых случились события. Предположим, что все эти соединения оказались в одном процессе. Значит этот процесс будет обрабатывать эти события следующим образом: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 последовательно. В результате наибольшая задержка будет при обработке 12-го события и она будет равна суммарному времени затраченному на обработку всех предыдущих 11 событий.

А теперь представьте, что у нас есть 4 ядра и запущено 4 рабочих процесса и соединения были распределены равномерно по этим процессам (по 3 соединения на процесс).

Тогда для каждого из процессов обработка всех событий будет выглядеть так:
CPU1: 1, 2, 3.
CPU2: 1, 2, 3.
CPU3: 1, 2, 3.
CPU4: 1, 2, 3.

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

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

Erratum: коллеги поправили, там не round-robin, а хэш от ip и порта сервера и клиента. Что ещё хуже, т.к. теоретически можно попытаться умышленно обмануть механизм и упростить DoS-атаку, если всё это смотрит в интернет.

Как минимум потому, что у reuseport есть своя проблема с распределением нагрузки по рабочим процессам. Там соединения раскидываются просто в режими round-robin и это неплохо работает, когда все соединения абсолютно одинаковы и дают одинаковую нагрузку. В реальности же часто бывает так, что соединения и запросы пользователей в них могут быть сильно разными. Могут быть соединения в которых вообще нет никаких запросов - их браузер может открывать "про запас", а могут быть тяжелые соединения, создающие большую нагрузку на рабочий процесс. Одни соединения долгоживущие, а другие короткоживущие. В итоге процесс, которому попались тяжелые долгоживущие соединения - будет перегружен, а те, в которых больше легких и короткоживущих - недогружены.

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

Information

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