心跳设置与钩子
介绍
心跳的作用是在长连接中保持通信的活跃状态。
当客户端和服务端建立了长连接后,如果客户端长时间没有向服务器发送消息,服务端通常会断开该长连接。 客户端可以通过定期向服务器发送心跳包来保持连接的活跃状态,以防止被服务器断开。
心跳包是一种空的数据包,通过定期发送心跳包来模拟一次正常的数据传输,从而保证连接的连通性。 对外服的职责之一是保持与用户的长连接,所以心跳相关的会在对外服做设置。
Example Source Code
see https://github.com/iohao/ionet-examples
path : ionet-cookbook-code
- MyIdleHook
- MyExternalServer
开启心跳机制
只有对外服需要开启心跳,我们可以在建构对外服时, 将心跳相关的设置添加到 setting 中就可以了,下面这个示例在 idle 方法中演示了心跳整体时间的设置。
- code 4,创建 IdleProcessSettingBuilder,用于心跳相关的设置。
- code 5,心跳整体时间设置,包括 readerIdleTime、writerIdleTime、allIdleTime。
- code 7,添加 IdleProcessSettingBuilder 到对外服构建器中。
public ExternalServerBuilder builder(int port, ExternalJoinEnum joinEnum) {
var builder = ExternalMapper.builder(port, joinEnum);
var idleProcessSettingBuilder = new IdleProcessSettingBuilder()
.setIdleTime(10);
builder.setIdleProcessSettingBuilder(idleProcessSettingBuilder);
return builder;
}
warning
如果开启了心跳机制,需要客户端在一定的时间间隔向服务器发送心跳消息,否则会被断开。
心跳钩子
IdleHook 心跳钩子接口提供了如下方法
- callback 方法,触发了 netty 心跳事件机制的回调方法。
- pongBefore 方法,将心跳消息响应给客户端前的回调钩子方法。
框架为 IdleHook 接口提供了一个默认实现类 DefaultSocketIdleHook,使用如下
public ExternalServerBuilder builder(int port, ExternalJoinEnum joinEnum) {
var idleProcessSettingBuilder = ...;
idleProcessSettingBuilder.setIdleHook(new DefaultSocketIdleHook())
...
}
自定义心跳钩子
MyIdleHook 是我们自定义的心跳钩子。
- code 12,把当前时间戳给到心跳接收端,让客户端的时间与服务器同步。
- code 17,使用默认实现的 callback,当返回 true 时,表示通知框架将当前用户的连接关闭。
- code 22,每秒更新一次当前时间。
public class MyIdleHook implements SocketIdleHook {
final DefaultSocketIdleHook defaultSocketIdleHook = new DefaultSocketIdleHook();
/** currentTimeMillis */
volatile byte[] timeBytes;
@Override
public void pongBefore(CommunicationMessage idleMessage) {
/*
* Set the time of the current server so that the time of the client and the server can be synchronized.
* cn: 设置当前服务器的时间,以便客户端与服务器的时间同步。
*/
idleMessage.setData(timeBytes);
}
@Override
public boolean callback(UserSession userSession, IdleStateEvent event) {
return defaultSocketIdleHook.callback(userSession, event);
}
public MyIdleHook() {
updateTime();
TaskKit.runInterval(this::updateTime, 1, TimeUnit.SECONDS);
}
private void updateTime() {
var data = LongValue.of(TimeKit.currentTimeMillis());
timeBytes = DataCodecManager.encode(data);
}
}