Пульсация

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

Функция пульсации имеет две основные цели:

  1. Периодическая отправка клиентом данных на сервер, чтобы избежать разрыва соединения из-за отсутствия общения в течение продолжительного времени из-за блокировки соединения фаерволлом определенного узла.
  2. Сервер может определить онлайн-статус клиента по пульсации. Если клиент в течение определенного времени не отправляет данных, это интерпретируется как его автономное отключение. Таким образом, можно обнаружить случаи аварийного отключения клиента (например, отключение питания, обрыв сети и т. д.).

Рекомендуемый интервал для пульсации:

Рекомендуется, чтобы клиент отправлял пульсацию не реже чем каждые 60 секунд, например, каждые 55 секунд.

Формат данных пульсации не имеет специальных требований, главное, чтобы сервер мог их распознать.

Пример пульсации

<?php
use Workerman\Worker;
use Workerman\Timer;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// Интервал пульсации 55 секунд
define('HEARTBEAT_TIME', 55);

$worker = new Worker('text://0.0.0.0:1234');

$worker->onMessage = function(TcpConnection $connection, $msg) {
    // Временно устанавливаем свойство lastMessageTime для соединения, чтобы запомнить время последнего получения сообщения
    $connection->lastMessageTime = time();
    // Другая бизнес-логика...
};

// После запуска процесса устанавливаем таймер, который будет запускаться каждые 10 секунд
$worker->onWorkerStart = function($worker) {
    Timer::add(10, function()use($worker){
        $time_now = time();
        foreach($worker->connections as $connection) {
            // Возможно это соединение еще не получало сообщения, поэтому lastMessageTime устанавливаем в текущее время
            if (empty($connection->lastMessageTime)) {
                $connection->lastMessageTime = $time_now;
                continue;
            }
            // Если интервал времени с последнего общения больше интервала пульсации, считаем, что клиент отключился, и закрываем соединение
            if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
                $connection->close();
            }
        }
    });
};

Worker::runAll();

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

Повторное подключение (важно)

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

Из-за того, что внешнее окружение сети может легко вызывать разрыв соединений, повторное подключение является обязательным функционалом для долгосрочных приложений (повторное подключение может выполнять только клиент, сервер не может это реализовать). Например, браузер веб-сокетов должен отслеживать событие onclose и устанавливать новое соединение при его возникновении (чтобы избежать возможной задержки). Чтобы быть более строгими, сервер также должен периодически отправлять пульсацию данных, и клиент должен периодически мониторить время пульсации сервера. Если за установленное время не получено пульсационные данные, соединение должно быть закрыто, и должно быть установлено новое соединение.