Heartbeat
Atenção: Aplicações de conexão longa devem adicionar heartbeats, caso contrário, a conexão pode ser forçosamente encerrada pelos nós de roteamento devido à ausência de comunicação por um longo período.
O heartbeat tem duas funções principais:
-
O cliente envia dados periodicamente ao servidor para evitar que a conexão seja encerrada por um firewall de algum nó devido à falta de comunicação por um longo período.
-
O servidor pode usar o heartbeat para verificar se o cliente está online. Se o cliente não enviar nenhum dado dentro do tempo estipulado, o servidor considera que o cliente está offline. Isso permite detectar eventos de desconexão extrema do cliente (como falta de energia, queda na internet, etc.).
Intervalo sugerido para o heartbeat:
Sugerimos que o cliente envie o heartbeat com um intervalo inferior a 60 segundos, como por exemplo 55 segundos.
Não há requisitos para o formato dos dados do heartbeat, desde que o servidor consiga reconhecê-lo.
Exemplo de Heartbeat
<?php
use Workerman\Worker;
use Workerman\Timer;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// Intervalo do heartbeat de 55 segundos
define('HEARTBEAT_TIME', 55);
$worker = new Worker('text://0.0.0.0:1234');
$worker->onMessage = function(TcpConnection $connection, $msg) {
// Define temporariamente um atributo lastMessageTime para o connection, para registrar o tempo da última mensagem recebida
$connection->lastMessageTime = time();
// Outras lógicas de negócio...
};
// Após o processo ser iniciado, define um temporizador que executa a cada 10 segundos
$worker->onWorkerStart = function($worker) {
Timer::add(10, function()use($worker){
$time_now = time();
foreach($worker->connections as $connection) {
// É possível que a conexão ainda não tenha recebido mensagens, neste caso, define lastMessageTime como o tempo atual
if (empty($connection->lastMessageTime)) {
$connection->lastMessageTime = $time_now;
continue;
}
// Se o intervalo desde a última comunicação for maior que o intervalo do heartbeat, considera-se que o cliente está offline e fecha a conexão
if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
$connection->close();
}
}
});
};
Worker::runAll();
A configuração acima faz com que, se o cliente não enviar nenhum dado ao servidor por mais de 55 segundos, o servidor considere que o cliente está offline, feche a conexão e acione o evento onClose.
Reconexão após desconexão (Importante)
Independentemente de o cliente ou o servidor enviar heartbeats, a conexão pode ser interrompida. Por exemplo, o JavaScript pode ser pausado quando o navegador é minimizado, quando a aba do navegador é trocada, quando o computador entra em modo de suspensão, quando dispositivos móveis trocam de rede, a sinalização é fraca, o telefone é desligado, o aplicativo do telefone vai para o segundo plano, falhas em roteadores e desconexões proativas de serviços, entre outros. Especialmente em ambientes externos, onde muitos nós de roteamento tendem a limpar conexões inativas por mais de 1 minuto, isso é o que justifica a recomendação de um intervalo de heartbeat inferior a 1 minuto.
As conexões em ambientes externos são facilmente desconectadas, portanto a reconexão após desconexão é uma funcionalidade essencial para aplicações de conexão longa (apenas o cliente pode realizar a reconexão, o servidor não pode implementá-la). Por exemplo, o WebSocket do navegador deve escutar o evento onclose e, ao ocorrer o onclose, estabelecer uma nova conexão (para evitar a necessidade de um colapso, a nova conexão deve ser estabelecida com um atraso). De forma mais rigorosa, o servidor também deve periodicamentem enviar dados de heartbeat, e o cliente deve monitorar se os dados de heartbeat do servidor estão dentro do tempo estipulado. Se o cliente não receber os dados de heartbeat do servidor dentro do tempo definido, a conexão deve ser considerada como encerrada, e deve executar o close para fechar a conexão e estabelecer uma nova conexão.