Одной из ключевых особенностей PHP является — легкость для разработчика в написании первой программы. Во многих мануалах для старта разработки сокращают информацию о web-сервере до минимума, например, запустите openserver или скопируйте собранный докер образ, где уже будет все настроено и просто перейдите по адресу http://localhost. Все это приводит к сужению знаний общей картины как работает web-приложение, что негативно влияет на репутацию разработчиков на этом языке программирования в целом. В прошлой статье я обещал рассказать о web-серверах для PHP, как раз для того, чтобы расширить кругозор тех людей, кто пропустил эту тему и постараться раскрыть ее максимально простым и понятным языком.
Язык программирования PHP является интерпретируемым, в нем нет встроенного production-ready http сервера. Чаще всего на своей практике встречал:
-
Apache с mod_php или mod_fcgid модулем
-
Nginx + PHP-FPM
-
RoadRunner
Все они работают по простому принципу:
-
Слушается IP + port
-
Запускаются worker с интерпретатором
-
Передают входящие запросы worker’у, а по полученный ответ передают запросившему клиенту.
В теории все просто, но как и в любом деле есть свои нюансы.
В большинстве случаев за работу web-сервера отвечают системные администраторы, но эта статья больше для разработчиков, поэтому будем рассматривать только моменты связанные с разработкой, а не полной настройкой web-серверов.
Во всех типах web-серверов в конфигурации указываются правила, по которым находится точка входа в приложение, а также какие файлы являются интерпретируемыми, а какие статичными. В популярных PHP-framework’ах все сводится к одной точке входа, на которую попадают все запросы, а роутинг уже настраивается внутри приложения. После запуска web-сервера вместе с ним запускаются worker’ы. В один момент времени 1 worker может обрабатывать только 1 запрос. Это самое важное, что должен понимать разработчик, когда пишет код на PHP.
Apache и Nginx + PHP-FPM — при каждом запросе запускают скрипт с точки входа, заново подключают все подключаемые файлы и исполняют код, в случае с RoadRunner запускается новый cli процесс, в котором приложение может быть уже инициализировано. Как правило для ускорения работы используют opcache, он не является частью web-сервера, а является частью интерпретатора. Его основная функция — сохранять полученный от Zend VM opcode для дальнейшего его переиспользования.
Для снижения накладных расходов самого web-сервера на обработку запроса они изначально, согласно конфигурации, поднимают определенное количество worker. Исходя из этого, легко можно сделать вывод, что в один момент времени, web-сервер может обработать количество запросов равное количеству worker’ов, поэтому основная задача разработчика — освободить worker как можно быстрее, чтобы он мог обработать новый запрос.
Важно, что worker освобождается не в момент отправки ответа клиенту, а в момент, когда последняя инструкция кода была выполнена, то есть, вызваны все __destruct функции, выполнены все shutdown функции.
Для более качественной работы важно для каждого внешнего соединения указывать timeout самого соединения и timeout ответа на запрос. Для защиты от этого в PHP есть специальные настройки — max_execution_time (по умолчанию: 60 секунд), default-socket-timeout (по умолчанию: 60 секунд).
Что происходит, если у нас есть 20 воркеров, а пришло 50 запросов:
-
В работу будет взято 20 запросов, остальные 30 запросов — уйдут в очередь либо будут сразу отброшены web-сервером.
-
После обработки каждого запроса будет браться следующий запрос, уменьшая очередь ожидания
-
Если время ожидания и время выполнения запроса укладывается в общее время ожидания запроса web-сервером, то такие запросы будут обработаны, если же нет, то такие запросы, в основном, вернут ошибку 504 Gateway time out.
Что важно знать разработчику об Apache
.htaccess файлы являются частью конфигурации web-сервера и подключаются при каждом запросе, поэтому любые изменения в этих файлах приведут к изменению работы web-сервера.
Что важно знать разработчику о Nginx + PHP-FPM сервере
PHP-FPM — не является web-сервером, он является FastCGI Process Manager, поэтому вместе с ним необходим Nginx, который проксирует http-запросы в PHP-FPM.
Что важно знать разработчику о RoadRunner
RoadRunner — web-сервер написанный на golang. Он запускает cli-команду воркера и по сокету общается с запущенным скриптом. В зависимости от настроек он может перезапускать worker’ы по разным параметрам, но чаще всего запущенный worker обрабатывает более одного запроса. Все это приводит к тому, что необходимо следить:
-
За открытыми соединениями (может быть база данных, брокер сообщений, сокет для отправки логов и любое другое открытое соединение);
-
За статическими переменными, они будут актуальны только в рамках работы одного worker’а, но доступны в разных запросах, в идеале отказаться от них или же использовать с полным пониманием как работает позднее статическое связывание;
-
Очисткой памяти (тут есть нюансы, сам Zend VM, в котором работает код не идеален, поэтому утечки памяти могут быть при плохой организации кода и от них никак не избавитесь).
В реальных проектах чаще всего PHP скрипт ожидает ответа от внешних систем, например, базы данных. В момент ожидания PHP почти не потребляет ресурсов процессора, но использует зарегистрированную оперативную память. Пример кода выложил на github:
В результате теста у меня получилось:
User CPU |
6.048ms |
System CPU |
3.629ms |
Memory |
0.38MB |
Memory real usage |
2.00MB |
Total time |
3.009s |
Это говорит о том, что мы заняли воркер на 3 секунды, а в действительности использовали только 3.5мс времени процессора, все остальное время просто занимали worker.
Заключение
Все это говорит о том, что базово PHP не подходит для того, чтобы использоваться в highload проектах, в чистом виде. Opcache с preload, jit, roadrunner, которые помогают сократить момент инициализации — никак не решают проблемы с синхронным выполнением запросов во внешние системы, а 90% кода как раз состоит из того, чтобы получить данные из stateful системы, преобразовать и выдать результат и/или передать дальше в stateful систему. Но не спешите расстраиваться и отвергать язык, в большинстве случаев даже в относительно крупных проектах не требуется выдерживать большие нагрузки и допустима обработка запросов в пределах одной секунды и написание бизнес логики на этом языке довольно простое.
PS. Лично для меня PHP стал близким ЯП, хотя я работаю с разными языками, golang, java, c-lang… это отличные языки для своих целей, в каждом есть свои плюсы и минусы. Понимая суть, как работают разные языки и какие есть недостатки у PHP, я хочу продолжить разговор о языке и его возможностях. Например, у меня получилось с помощью swoole 5й версии (предыдущие версии действительно были плохие) создать web-сервер, который обращаясь к базе данных и выдавая ответ может одновременно обрабатывать большое количество соединений с отсутствием проблемы с обработкой запросов в один момент времени.
ссылка на оригинал статьи https://habr.com/ru/articles/832040/
Добавить комментарий