Heartbeat

Note: Long connection applications must implement heartbeats; otherwise, the connection may be forcibly dropped by routing nodes due to long periods of inactivity.

The heartbeat serves two main purposes:

  1. The client periodically sends data to the server to prevent the connection from being closed by some nodes' firewalls due to prolonged inactivity.

  2. The server can use the heartbeat to determine if the client is online. If the client does not send any data within a specified timeframe, it is considered offline. This allows for detection of client disconnections caused by extreme situations (power outages, network failures, etc.).

Recommended heartbeat interval:

It is suggested that the client sends heartbeats at intervals of less than 60 seconds, for example, every 55 seconds.

The heartbeat data format is not specified; as long as the server can recognize it, it is sufficient.

Heartbeat Example

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

// Heartbeat interval of 55 seconds
define('HEARTBEAT_TIME', 55);

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

$worker->onMessage = function(TcpConnection $connection, $msg) {
    // Temporarily set a lastMessageTime attribute on the connection to record the last message received time
    $connection->lastMessageTime = time();
    // Other business logic...
};

// Set up a timer that runs every 10 seconds after the process starts
$worker->onWorkerStart = function($worker) {
    Timer::add(10, function() use ($worker) {
        $time_now = time();
        foreach($worker->connections as $connection) {
            // It's possible that this connection has not received any messages yet, so set lastMessageTime to the current time
            if (empty($connection->lastMessageTime)) {
                $connection->lastMessageTime = $time_now;
                continue;
            }
            // If the last communication time interval is greater than the heartbeat interval, consider the client offline and close the connection
            if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
                $connection->close();
            }
        }
    });
};

Worker::runAll();

The above configuration implies that if the client does not send any data to the server for more than 55 seconds, the server will consider the client offline, close the connection, and trigger onClose.

Reconnection (Important)

Whether the client sends a heartbeat or the server does, the connection may still drop. For instance, JavaScript in the browser may pause when the browser is minimized, when switched to another tab, or when the computer enters sleep mode. Mobile devices may experience network changes, signal loss, or the app going to the background. Other potential issues include routing failures or intentional disconnections. This is particularly relevant in complex external network environments where many routing nodes clean up inactive connections after one minute, which is why a heartbeat interval of less than one minute is recommended.

Connections in external networks are easily dropped, so reconnection is a critical function for long connection applications (only the client can handle reconnection; the server cannot implement it). For example, the browser's WebSocket should listen for the onclose event and establish a new connection when this occurs (with a delay to avoid rapid reconnections). More strictly, the server should also periodically send heartbeat data, and the client needs to monitor the server's heartbeat data for timeouts. If the server's heartbeat data is not received within the specified time, it should consider the connection closed, execute close to terminate the connection, and establish a new connection.