Сердцебиение

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

Основные функции сердцебиения:

  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) {
    // Временно установить у connection свойство lastMessageTime, чтобы записать время последнего полученного сообщения
    $connection->lastMessageTime = time();
    // Другие бизнес-логики...
};

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

Worker::runAll();

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

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

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

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