Что необходимо знать перед разработкой
При разработке приложений с помощью Workerman вам нужно понять следующее:
I. Различия между разработкой на Workerman и обычной PHP-разработкой
Помимо того, что переменные и функции, связанные с протоколом HTTP, не могут быть использованы напрямую, разработки на Workerman не сильно отличаются от обычной PHP-разработки.
1. Различие в протоколах прикладного уровня
- Обычная PHP-разработка, как правило, основана на протоколе HTTP, который уже обрабатывается веб-сервером.
-
Workerman поддерживает различные протоколы, в данный момент встроены протоколы HTTP, WebSocket и др. Workerman рекомендует разработчикам использовать более простые пользовательские протоколы для общения.
- Для разработки на HTTP-протоколе смотрите часть о Http-сервисе
2. Различия в жизненном цикле запросов
- PHP в веб-приложениях освобождает все переменные и ресурсы после одного запроса.
- Программа, разработанная на Workerman, остается в памяти после первой загрузки и анализа, что позволяет не освобождать определения классов, глобальные объекты и статические члены классов для повторного использования в будущем.
3. Будьте внимательны к повторному определению классов и констант
- Поскольку Workerman кеширует скомпилированные PHP-файлы, следует избегать многократного использования
require/includeдля файлов с определениями классов или констант. Рекомендуется использоватьrequire_once/include_onceдля загрузки файлов.
4. Будьте внимательны к освобождению ресурсов соединений в паттерне Singleton
- Поскольку Workerman не освобождает глобальные объекты и статические члены классов после каждого запроса, в паттернах Singleton, таких как базы данных, экземпляр базы данных (внутри которого находится сокет подключения к базе данных) часто сохраняется в статических членах, что позволяет Workerman повторно использовать этот сокет в течение жизненного цикла процесса. Необходимо учитывать, что сервер базы данных может самостоятельно закрыть сокет, если обнаружит, что соединение не активно в течение определенного времени. В этот момент повторное использование этого экземпляра базы данных приведет к ошибке (ошибка может быть аналогична mysql gone away). Workerman предоставляет класс базы данных с функцией переподключения, которую разработчики могут использовать непосредственно.
5. Не используйте операторы exit и die
- Workerman работает в режиме командной строки PHP, и вызов операторов
exitилиdieзавершает текущий процесс. Хотя после выхода дочерний процесс будет немедленно воссоздан, это может оказать влияние на бизнес-процессы.
6. Для применения изменений в коде необходимо перезагрузить сервис
Поскольку Workerman работает в постоянной памяти, определения классов и функций загружаются один раз и не считываются снова с диска, поэтому после каждого изменения бизнес-кода необходимо перезагружать сервис для применения изменений.
II. Основные концепции, которые необходимо понимать
1. Протокол передачи данных TCP
TCP - это ориентированный на соединение, надежный протокол передачи данных на транспортном уровне, основанный на IP. Важной особенностью протокола TCP является то, что он основан на потоках данных: запрос клиента будет последовательно отправляться серверу, и полученные данные могут не представлять собой полный запрос или представлять собой несколько связанных запросов. Это требует от нас различать границы каждого запроса в потоке непрерывных данных. Протоколы прикладного уровня определяют набор правил для границ запроса, чтобы избежать путаницы в данных запроса.
2. Протокол прикладного уровня
Протокол прикладного уровня (application layer protocol) определяет способ взаимной передачи сообщений процессами приложений, работающими на различных системах (клиент, сервер), например, HTTP, WebSocket. Например, простой протокол прикладного уровня может выглядеть так: {"module":"user","action":"getInfo","uid":456}\n". Этот протокол использует "\n" (обратите внимание, что здесь "\n" обозначает перевод строки) для обозначения конца запроса, содержание сообщения представляет собой строку.
3. Короткое соединение
Короткое соединение подразумевает, что при наличии обмена данными устанавливается соединение, которое разрывается после завершения передачи данных, т.е. каждое соединение используется только для одной единицы работы. Сервисы HTTP большинства веб-сайтов обычно используют короткие соединения.
Разработку приложений на коротком соединении можно рассмотреть в главе основного процесса разработки
4. Долгое соединение
Долгое соединение означает, что на одном соединении можно последовательно отправлять несколько пакетов данных.
Обратите внимание: долгосрочные приложения должны использовать сердечные битки, иначе соединение может быть разорвано файерволом маршрутизатора из-за длительного отсутствия активности.
Долгое соединение часто используется в случаях частого общения «точка-точка». Каждое TCP-соединение требует три шага для установления, что требует времени. Если каждое действие требует сначала установить соединение, а затем выполнить действие, это значительно замедляет скорость обработки. Поэтому долгие соединения не разрываются после каждого действия; вместо этого данные отправляются непосредственно при следующей обработке, не нужно устанавливать TCP-соединение. Например, для подключения к базе данных используется длительное соединение, и частое использование коротких соединений может привести к ошибкам сокета, а частое создание сокетов также затрачивает ресурсы.
Когда необходимо активно отправлять данные клиенту, например, для приложений с чатами, мгновенными играми, мобильными уведомлениями и др., требуется долгосрочное соединение.
Разработку долгих соединений можно рассмотреть в процессе разработки Gateway/Worker
5. Плавная перезагрузка
Обычный процесс перезагрузки сперва останавливает все процессы, а затем создает совершенно новые сервисные процессы. В этот момент будет короткий период времени, когда ни один процесс не предоставляет услуги, что может привести к временной недоступности сервиса, особенно в высоконагруженных ситуациях, что непременно приведет к сбою запросов.
Плавная перезагрузка, с другой стороны, не останавливает все процессы сразу, а останавливает их по одному, сразу же создавая новый процесс, который заменяет остановленный, пока все старые процессы не будут заменены.
Чтобы выполнить плавную перезагрузку в Workerman, можно использовать команду php your_file.php reload, что позволит обновить приложение без ухудшения качества обслуживания.
Обратите внимание: файлы, загружаемые в обратном вызове on{...}, будут автоматически обновлены после плавной перезагрузки, тогда как файлы, загружаемые недиректно в сценарии запуска или жестко закодированные, не будут автоматически обновлены при выполнении reload.
III. Различия между главным процессом и дочерними процессами
Важно учитывать, в каком процессе выполняется код. В общем случае код, выполняемый до вызова Worker::runAll();, работает в главном процессе, а код, выполняемый в обратных вызовах onXXX, относится к дочерним процессам. Важно помнить, что код, написанный после Worker::runAll(); никогда не будет выполнен.
Например, следующий код:
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// Выполняется в главном процессе
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// Присвоение происходит в главном процессе
$tcp_worker->onMessage = function(TcpConnection $connection, $data)
{
// Эта часть выполняется в дочернем процессе
$connection->send('hello ' . $data);
};
Worker::runAll();
Обратите внимание: Не инициализируйте соединения с базами данных, memcache, redis и т. д. в главном процессе, так как соединения, инициализированные в главном процессе, могут быть автоматически унаследованы дочерними процессами (особенно при использовании Singleton). Все процессы будут использовать одно и то же соединение, и данные, возвращаемые сервером через это соединение, могут быть доступны во множестве процессов, что приведет к путанице с данными. Аналогично, если любой из процессов закроет соединение (например, когда главный процесс завершает работу в режиме демона), все дочерние процессы также будут закрыты, что приведет к непредсказуемым ошибкам, таким как ошибка mysql gone away.
Рекомендуется инициализировать соединения в onWorkerStart.