netty心跳检测
一 什么是心跳检测机制
心跳是指,在TCP长连接中 客户端和服务端定期的互相发送数据包, 这样可以确保服务的正确运行,保证服务在线和TCP长连接的可靠性;通常的心跳实现机制是客户端定期的向服务端发送数据包,服务端接收到数据后进行应答,这样就保证了TCP的长连接;当然也有做法是服务端做心跳,如果客户端没有应答,就关闭对应的连接,节省资源,但是这种情况毕竟罕见!
二 netty 心跳工作原理
netty 中 进行实现心跳机制是,当客户端写空闲时就可以向服务端发送数据包,服务端收到心跳包后进行回复;关键点就是如何判定 客户端 是处于写空闲状态;netty中提供了 IdleStateHandler 的处理器;可以监听通道的读写状态;
- readerIdleTime表示 读超时
- writerIdleTime 表示写超时
- allIdleTime 表示 读写超时
- unit 表示 时间单位
public IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) {
this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
}
三 netty 客户端实现心跳机制
在 客户端连接时启动加入 监听事件
pipeline.addLast("ping", new IdleStateHandler(30, 20, 90, TimeUnit.SECONDS));
表示 读 30 秒空闲, 写 20 秒空闲;读写 90 秒空闲
public void connect(int port, String host) throws InterruptedException {
// 创建线程组
NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
// netty启动辅助类
Bootstrap bootstrap = new Bootstrap();
//
bootstrap.group(nioEventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 处理IO事件
ChannelPipeline pipeline = socketChannel.pipeline();
// 心跳检测
pipeline.addLast("ping", new IdleStateHandler(30, 20, 90, TimeUnit.SECONDS));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
//
pipeline.addLast(new NettyClientHandler());
}
});
// 异步操作
ChannelFuture connect = bootstrap.connect(host, port).sync();
// 关闭客户端
connect.channel().closeFuture().sync();
// 退出线程组
nioEventLoopGroup.shutdownGracefully();
}
然后 在处理器中 实现 userEventTriggered 方法对当前的通道状态进行判定,然后根据不同的状态发送心跳包;在客户端我们只需要是写空闲的时候发送心跳包即可;
@Slf4j
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
public NettyClientHandler() {
super();
}
// ......
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
switch (e.state()) {
case READER_IDLE:
System.out.println("读空闲");
break;
case WRITER_IDLE:
// 写空闲,发送心跳
System.out.println("写空闲,发送心跳包");
ctx.writeAndFlush("1");
break;
case ALL_IDLE:
System.out.println("读写空闲");
break;
default:
break;
}
}
super.userEventTriggered(ctx, evt);
}
}
在测试代码中,客户端发送心跳包 1; 服务端回复 0;
客户端效果图如下
服务端效果图如下