Что именно представляет собой модель предварительного веб-сервера?
Я хочу знать, что именно это означает, когда веб-сервер описывает себя как предварительный веб-сервер. У меня есть несколько примеров, таких как единорог для рубина и gunicorn для питона.
Более конкретно, это вопросы:
- Какую проблему решает эта модель?
- Что происходит, когда предварительно запускается предварительный веб-сервер?
- Как он обрабатывает запрос?
Кроме того, более конкретный вопрос для единорога /gunicorn:
Допустим, у меня есть веб-приложение, которое я хочу запустить с (g) единорогом. При инициализации веб-приложение выполнит некоторые действия по инициализации (например, заполнит дополнительные записи базы данных). Если я настрою (g) единорога с несколькими работниками, будет ли запускаться процесс инициализации несколько раз?
2 ответа
Предварительное разветвление в основном означает, что мастер создает вилки, которые обрабатывают каждый запрос. Вилка - это совершенно отдельный процесс *nix.
Обновление в соответствии с комментариями ниже.
pre
вpre-fork
означает, что эти процессы разветвляются до поступления запроса. Однако они обычно могут увеличиваться или уменьшаться по мере увеличения и уменьшения нагрузки.
Предварительная разветвленность может использоваться, когда у вас есть библиотеки, которые НЕ являются потокобезопасными. Это также означает, что проблемы в запросе, вызывающие проблемы, будут влиять только на процесс, которым они обрабатываются, а не на весь сервер.
Инициализация, запускаемая несколько раз, зависит от того, что вы развертываете. Однако обычно для каждого процесса существуют пулы соединений и тому подобное.
В поточной модели мастер будет создавать более легкие потоки для отправки запросов. Но если поток вызывает серьезные проблемы, он может иметь последствия для основного процесса.
С такими инструментами, как Nginx, Event MPM в Apache 2.4 или gevent (которые можно использовать с Gunicorn), они являются асинхронными, то есть процесс может обрабатывать сотни запросов, не блокируя их.
Как работает «модель pre-fork worker»?
- Главный процесс: существует главный процесс, который порождает и убивает рабочих в зависимости от нагрузки и мощности оборудования. Большее количество входящих запросов приведет к тому, что мастер порождает больше рабочих процессов, вплоть до достижения «аппаратного предела» (например, все загруженные ЦП), после чего устанавливается очередь.
- Рабочие: работника можно понимать как экземпляр вашего приложения/сервера. Итак, если есть 4 воркера, ваш сервер загружается 4 раза. Это означает, что он занимает в 4 раза больше «базовой памяти», чем только один рабочий процесс, если только вы не используете волшебство с общей памятью.
- Инициализация: ваша логика инициализации должна быть достаточно стабильной для учета нескольких серверов. Например, если вы пишете записи в базе данных, проверьте, есть ли они уже там, или добавьте задание установки перед вашим сервером приложений.
- Pre-fork: «pre» в pre-fork означает, что мастер всегда добавляет немного больше емкости, чем требуется в настоящее время, так что, если нагрузка увеличивается, система «уже готова». Таким образом, он предварительно порождает некоторых рабочих. Например, в библиотеке apache вы управляете этим с помощью
MinSpareServers
имущество. - Запросы: запросы (дескрипторы соединения TCP) передаются от главного процесса к дочерним.
Какую проблему решают pre-fork серверы?
- Многопроцессорность . Если у вас есть программа, которая может работать только с одним ядром ЦП, вы потенциально теряете часть мощности своего оборудования, создавая только один сервер. Раздвоенные рабочие решают эту проблему.
- Стабильность: при сбое одного рабочего процесса главный процесс не затрагивается. Он может просто породить нового рабочего.
- Потокобезопасность : поскольку ваш сервер действительно загружается несколько раз в отдельных процессах, вам не нужно беспокоиться о потокобезопасности (поскольку потоков нет). Это означает, что это подходящая модель, когда у вас есть код, не поддерживающий потоки, или вы используете библиотеки, не поддерживающие потоки.
- Скорость: поскольку дочерние процессы не разветвляются (порождаются) сразу, когда это необходимо, а упреждающе, сервер всегда может быстро реагировать.
Альтернативы и примечания
- Оркестрация контейнеров. Если вы знакомы с инструментами контейнеризации и оркестровки контейнеров, такими как kubernetes , вы заметите, что многие проблемы решаются и с их помощью. Kubernetes порождает несколько модулей для многопроцессорной обработки, он имеет такую же (или лучшую) стабильность и такие вещи, как «горизонтальные автоскейлеры модулей», которые также порождают и уничтожают рабочие процессы.
- Многопоточность: сервер может создавать поток для каждого входящего запроса, что позволяет обрабатывать множество запросов «одновременно». Это значение по умолчанию для большинства веб-серверов, основанных на Java, поскольку Java изначально имеет хорошую поддержку потоков. Хорошая поддержка означает, что потоки работают действительно параллельно на разных ядрах процессора. Потоки Python, с другой стороны, не могут по-настоящему распараллеливаться (= распространять работу на несколько ядер) из-за GIL (глобальная блокировка интерпретатора), они только предоставляют средства для переключения контекста. Подробнее об этомздесь . Вот почему для серверов Python так популярны «префоркеры», такие как gunicorn, и люди, пришедшие с Java, возможно, никогда раньше не слышали о такой вещи.
- Асинхронная/неблокирующая обработка : если ваши серверы проводят много времени в «ожидании», например дисковый ввод-вывод, HTTP-запросы к внешним службам или запросы к базе данных, то многопроцессорность может быть не тем, что вам нужно. Вместо этого подумайте о том, чтобы сделать ваш код «неблокирующим», что означает, что он может обрабатывать множество запросов одновременно. Системы на основе Async / await (сопрограммы), такие как fastapi (сервер asgi) в python, Go или nodejs, используют этот механизм, так что даже один сервер может обрабатывать множество запросов одновременно.
- Задачи, привязанные к ЦП: Если у вас есть задачи, привязанные к ЦП, упомянутая выше неблокирующая обработка не сильно поможет. Затем вам понадобится какой-то способ многопроцессорной обработки для распределения нагрузки на ядра вашего ЦП, как упомянутые выше решения, а именно: оркестровка контейнеров, многопоточность (в системах, которые допускают настоящее распараллеливание) или... предварительно разветвленные рабочие процессы.