长连接说简单一点就是不会断的连接 ? ,可以使用心跳包进行维持
心跳是什么?
顾名思义就是心脏的跳动,可以用来判断一个事物的生和死,Swoole 中的心跳是指用来判断一个连接是正常还是断开的
fd 是什么?
fd 学名是文件描述符(file descriptor),Swoole Server 中$fd 是 TCP 客户端连接的标识符,在 Server 实例中是唯一的,在多个进程内不会重复
fd 是一个自增数字,范围是 1 ~ 1600 万,fd 超过 1600 万后会自动从 1 开始进行复用
当连接关闭后 fd 会被新进入的连接复用,正在维持的 TCP 连接 fd 不会被复用
可以使用Server->getClientInfo
函数来获取连接的信息,看一下我们可以通过 fd 获取到什么信息
var_dump($serv->getClientInfo($fd));
打印后的数据为:
代码语言:javascript复制array(10) {
["server_port"]=> // 服务端端口
int(9501)
["server_fd"]=> // fd
int(4)
["socket_fd"]=>
int(11)
["socket_type"]=>
int(1)
["remote_port"]=> // 客户端连接的端口
int(49152)
["remote_ip"]=> // 客户端连接的 IP 地址
string(9) "127.0.0.1"
["reactor_id"]=> // 来自哪个 Reactor 线程
int(0)
["connect_time"]=> // 客户端连接到 Server 的时间,单位秒,由 master 进程设置
int(1562741559)
["last_time"]=> // 最后一次收到数据的时间,单位秒,由 master 进程设置
int(1562741559)
["close_errno"]=> // 连接关闭的错误码,如果连接异常关闭,close_errno 的值是非零
int(0)
}
为什么要心跳?
当我们要关闭客户端连接时,我们可以在业务层对 fd 发起关闭连接的操作,以 Swoole 为例:
代码语言:javascript复制$serv->close($fd);
Swoole 会有 onClose 回调,之前我们也说了连接关闭后 fd 会被新进入的连接复用
正常情况下客户端中断 TCP 连接时,会发送一个 FIN 包,进行 4 次断开握手来通知服务器。但一些异常情况下,如客户端突然断电断网或者网络异常,服务器可能无法得知客户端已断开连接
尤其是移动网络,TCP 连接非常不稳定,所以需要一套机制来保证服务器和客户端之间连接的有效性,所以就有了心跳机制
什么是心跳机制?
心跳机制就是业务层来提供一个连接是否存活的一个方法,让系统能判定一个连接是否失效
一般有两种实现方式:
- 客户端定时发送一个心跳包,告诉服务器我还活着,服务器定时检测所有客户端列表,看他们最后一个心跳包的时间是否过长,如果过长,则认为已无心跳,判定为死连接,主动关闭这个连接
- 服务器定时询问所有的客户端,你们还活着么?如果活着,给我个回馈,没得到回馈的客户端,主动关闭这个连接
两种心跳方案有什么区别?
第一种方案,对服务器和网络的压力更小,而且更具有灵活性,但需要客户端配合定时发送心跳包
第二种方案,对服务器和网络压力更大,不建议使用
在 Swoole 中如何实现?
Swoole 扩展本身内置了这种机制,开发者只需要配置一个参数即可启用。Swoole 在每次收到客户端数据会记录一个时间戳,当客户端在一定时间内未向服务器端发送数据,那服务器会自动切断连接
使用方式
在 Server 启动时增加两个参数
代码语言:javascript复制$serv->set(array(
'heartbeat_check_interval' => 5,
'heartbeat_idle_time' => 10,
));
设置了这两个参数后,Swoole 底层将会创建心跳检测线程,通过定时轮询所有的连接,来判断连接的生死,所以 Swoole 的心跳不会堵塞任何业务逻辑
上面的设置就是每 5 秒侦测一次心跳,一个 TCP 连接如果在 10 秒内未向服务器端发送数据,将会被切断
配置建议
建议heartbeat_idle_time
为heartbeat_check_interval
的两倍多一点
这个两倍是为了进行容错,允许丢一个包,而多一点是考虑到网络的延时
你可以跟据实际的业务来调整这个容错率(允许丢几个包)
在客户端发送心跳包
使用定时器定时向服务端发送心跳
代码语言:javascript复制SwooleTimer::tick(3000, function () use ($client) {
$data = "heartbeat";
$client->send($data);
});
服务端和客户端示例代码:https://github.com/sy-records/learn-swoole/tree/master/heartbeat
沈唁志,一个PHPer的成长之路!
任何个人或团体,未经允许禁止转载本文:《Swoole中的长连接和心跳包》,谢谢合作!