云迈博客

您现在的位置是:首页 > 后端开发 > PHP > 正文

PHP

workerman环境下长连接的心跳机制与断线重连

wsinbol2020-10-14PHP847
引言心跳与重连心跳一般是指某端(绝大多数情况下是客户端)每隔一定时间向对端发送自定义指令,以判断双方是否存活,因其按照一定间隔发送,类似于心跳,故被称为心跳指令。如此看来,心跳就是用来维护

文章首发于 http://wsinbol.github.io/2020/10/14/workerman环境下长连接的心跳机制与断线重连/

引言

心跳与重连

心跳一般是指某端(绝大多数情况下是客户端)每隔一定时间向对端发送自定义指令,以判断双方是否存活,因其按照一定间隔发送,类似于心跳,故被称为心跳指令。

如此看来,心跳就是用来维护客户端与服务端双方都可用的检测工具。当检测到有一方不可用时,就应触发“重连”机制,以保证下次通讯的正常交流。

重连,顾名思义,即重新连接,常见于客户端重新发起,笔者尚未见到有服务端发起重连的例子。。。(书看少了还是真的没有???)

“心跳”与“重连”是相辅相成的。在密集型通讯系统中,双方频繁发布消息,此时的“心跳”就是没有意义的,甚至会成为一种累赘,“重连”也就更无从提起了!但是当客户端越来越多,服务端对于发送消息都难以为继时,就不得不释放一些资源,“踢那些懒惰的家伙下线”!当下线的客户端再次发送消息时,就要提供用户无感知的重连,保证用户体验。充分权衡好“心跳”与“重连”,才能以最小的服务资源发挥最大的能量,这样才能做出一个完美的及时通讯系统。

workerman

workerman是一个高性能的PHP socket 服务器框架,workerman基于PHP多进程以及libevent事件轮询库,PHP开发者只要实现一两个接口,便可以开发出自己的网络应用,例如Rpc服务、聊天室服务器、手机游戏服务器等。

下面,结合workerman环境描述“心跳”与“重连”的几种方案!

方案

啥都不动版

  • 服务端配置

注释有关心跳检测的代码,不开启心跳检测的相关配置,workerman默认即是该状态。

// 心跳间隔
// $gateway->pingInterval = 10;
// 可容忍心跳限制次数
// $gateway->pingNotResponseLimit = 2;
// 心跳数据
// $gateway->pingData = '{"type":"ping"}';
// $gateway->pingData = '';
  • 客户端配置

客户端不需要定时发送ping,客户端和服务端在相当长的一段时间内维持长链接的状态。(具体时间多长可能是浮动的,至少我看了一个上午它还没有断线。。。下午一觉醒来断了,不知是不是电脑息屏原因所致。Whatever,那不重要,反正不推荐使用这种方式!)

当客户端主动关闭该页面时,服务端能立刻感知该客户端下线,触发onclose事件!

不断发ping版

不断发ping包括两种形式:一种是客户端定时发送ping,一种是服务端主动发送ping。

客户端定时发ping

  • 服务端配置
// 心跳间隔
$gateway->pingInterval = 55;
// 可容忍心跳限制次数
$gateway->pingNotResponseLimit = 1;
// 心跳数据
$gateway->pingData = '';

以上配置含义是客户端连接 pingInterval*pingNotResponseLimit=55 秒内没有任何请求则服务端认为对应客户端已经掉线,服务端关闭连接并触发onClose回调。

  • 客户端描述
setTimeInterval(function(){
    ws.send('ping');
},3000);

服务端主动发ping

  • 服务端配置

$gateway = new Gateway("Websocket://0.0.0.0:8585");

$gateway->pingInterval = 55;

$gateway->pingNotResponseLimit = 0;

// 服务端定时向客户端发送的数据
$gateway->pingData = '{"type":"ping"}';

以上服务端会定时55秒给客户端发心跳数据{“type”:”ping”},而客户端不需要定时向服务端发送心跳数据。

其中pingNotResponseLimit = 0,代表服务端允许客户端不发送心跳,服务端不会因为客户端长时间没发送数据而断开连接。
如果pingNotResponseLimit = 1,则代表客户端必须定时发送心跳给服务端,否则pingNotResponseLimit*pingInterval=55秒内没有任何数据发来则关闭对应连接,并触发onClose。

  • 客户端描述

什么都不需要做!

断线重连版

该版的前提是服务端已经设置了客户端要在规定时间内发送ping包过来,如果在规定时间内未接收到客户端发送的ping,则服务端会主动将客户端踢下线,触发onclose事件。与此同时,客户端监听到close状态,则会触发断线重连机制。

  • setTimeout 定时触发形式(代码节选)
var tt = '';
var lockReconnect = false;//避免重复连接
function reconnect(url) {
    if(lockReconnect) {
    return;
    };
    lockReconnect = true;
    //没连接上会一直重连,设置延迟避免请求过多
    tt && clearTimeout(tt);
    tt = setTimeout(function () {
    createWebSocket(url);
    lockReconnect = false;
    }, 4000);
}
  • 借助工具断线重连

详细了解见 https://github.com/joewalnes/reconnecting-websocket

使用方法非常简单,引入ReconnectingWebSocket相关文件,将

var ws = new WebSocket('ws://....');

替换为:

var ws = new ReconnectingWebSocket('ws://....'); 即可实现断线重连!

支持的功能很多,但了解不多,不予置评!

理想主义版

先来说“心跳”,理想中的心跳是一个浮动的状态。当消息交互频繁时,心跳处于停滞状态,此时发送心跳还占用资源,故没必要。当一段时间内双方没有往来消息时,启动心跳机制,并且两次心跳之间的间隔程逐渐递增状态,同时要有一个阈值来约束,此后维持在阈值水平发包即可。

再看下“重连”,当确实因为不可抗拒的因素导致双方断开后,可根据是否有新的消息或者自身的消息发起建立重连机制,而不是断线后立即发起重连,毕竟重连后相当一段时间内无消息交互再被踢下线也是一种浪费。

一点拙见,欢迎讨论!

参考资料

  1. 为什么说基于TCP的移动端IM仍然需要心跳保活?
  2. 正确理解IM长连接的心跳及重连机制,并动手实现(有完整IM源码)

发表评论

评论列表

  • 这篇文章还没有收到评论,赶紧来抢沙发吧~