传统的心跳包设计,基本上是服务端和客户端同时维护 Scheduler,然后双方互相接收心跳包信息,然后重置双方的上下线状态表。此种心跳方式的设计,可以选择在主线程上进行,也可以选择在心跳线程中进行,由于在进行业务调用过程中,此种心跳包是没有必要进行发送的,所以在一定程度上会造成资源浪费。严重的甚至会影响业务线程的操作。但是在 Netty 中是通过检测链路的空闲与否在进行的。链路分为读操作空闲,写操作空闲,读写操作空闲。由于空闲检测本身只有在通道空闲的时候才进行检测,而不是固定频率的进行心跳包通讯,所以可以节省网络带宽,同时对业务的影响也很小

在 Netty 中空闲检测需要引入 IdleStateHandler,然后实现自己的心跳处理 Handler,本文中服务端与客户端均向对方发送心跳包。

一、服务端

1.1 编解码及 Handler

...

.childHandler(new ChannelInitializer<SocketChannel>() {

    @Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast("ping", new IdleStateHandler(10, 5, 10));
channel.pipeline().addLast("encoder", new NettyMessageEncoder());
channel.pipeline().addLast("decoder", new NettyMessageDecoder());
channel.pipeline().addLast("message", new MessageHandler());
channel.pipeline().addLast("heartbeat", new HeartbeatHandler());
}
}); ...

HeartbeatHandler 为 心跳处理 Handler

1.2 心跳处理 Handler

public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(HeartbeatHandler.class); private final AttributeKey<Integer> counterAttr = AttributeKey.valueOf(ChannelSupervise.COUNTER_ATTR);; @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt); if(evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent)evt;
switch (idleStateEvent.state()) {
case READER_IDLE:
NettyMessage<String> nettyMessage = new NettyMessage<>();
nettyMessage.setSessionId(0L);
nettyMessage.setType(NettyMessageTypeEnum.HEARTBEAT); ctx.writeAndFlush(nettyMessage).addListener(future -> {
// if(future.isSuccess()) {
// ctx.channel().attr(counterAttr).set(0);
// }else {
Integer counter = ctx.channel().attr(counterAttr).get();
counter = counter + 1;
LOGGER.info(ctx.channel().id().asShortText() + ",发送心跳: " + counter);
if(counter >= 3) {
ctx.close();
} else {
ctx.channel().attr(counterAttr).set(counter);
}
// }
});
break;
default:
break;
}
}
} @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(counterAttr).set(0);
ChannelSupervise.addChannel(ctx.channel());
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ChannelSupervise.removeChannel(ctx.channel());
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ctx.channel().attr(counterAttr).set(0);
ctx.fireChannelRead(msg);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
LOGGER.error("断开连接", cause);
ctx.close();
}
}

本例中,如果服务端连续发送三次心跳包,则认为客户端断开连接,使用 Netty 内置的 AttributeKey 计数 (本例中为方便测试注释掉部分代码,正常来说如果发送消息成功则证明客户端还在线,需要把计数重置为 0)。

二、客户端

2.1 编解码及 Handler

...

.handler(new ChannelInitializer<SocketChannel>() {

    @Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast("ping", new IdleStateHandler(5, 5, 3));
channel.pipeline().addLast("encoder", new NettyMessageEncoder());
channel.pipeline().addLast("decoder", new NettyMessageDecoder());
channel.pipeline().addLast("heartbeat", new HeartbeatHandler());
channel.pipeline().addLast("logger", new LoggingHandler(LogLevel.INFO));
}
}); ...

HeartbeatHandler 为 心跳处理 Handler

2.2 心跳处理 Handler

public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(HeartbeatHandler.class); @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if(evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt; switch (idleStateEvent.state()){
case WRITER_IDLE:
LOGGER.info("发送心跳包");
NettyMessage<String> nettyMessage = new NettyMessage<>();
nettyMessage.setSessionId(0L);
nettyMessage.setType(NettyMessageTypeEnum.HEARTBEAT);
ctx.writeAndFlush(nettyMessage).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
break;
default:
break;
}
} super.userEventTriggered(ctx, evt);
} ...
}

本例中,如果客户端发送心跳消息失败则断开连接。

参考

  1. Netty(一) SpringBoot 整合长连接心跳机制TCP-Heartbeat/)
  2. 微言Netty:分布式服务框架

完整代码:GitHub

Netty 心跳处理的更多相关文章

  1. 连接管理 与 Netty 心跳机制

    一.前言 踏踏实实,动手去做,talk is cheap, show me the code.先介绍下基础知识,然后做个心跳机制的Demo. 二.连接 长连接:在整个通讯过程,客户端和服务端只用一个S ...

  2. 聊聊心跳机制及netty心跳实现

    我们在使用netty的时候会使用一个参数,ChannelOption.SO_KEEPALIVE为true, 设置好了之后再Linux系统才会对keepalive生效,但是linux里边需要配置几个参数 ...

  3. NETTY 心跳机制

    最近工作比较忙,但闲暇之余还是看了阿里的冯家春(fengjiachun)的github上的开源代码Jupiter,写的RPC框架让我感叹人外有人,废话不多说,下面的代码全部截取自Jupiter,写了一 ...

  4. netty心跳检测机制

    既然是网络通信那么心跳检测肯定是离不开的,netty心跳检测分为读.写.全局 bootstrap.childHandler(new ChannelInitializer<SocketChanne ...

  5. netty心跳机制测试

    netty中有比较完善的心跳机制,(在基础server版本基础上[netty基础--基本收发])添加少量代码即可实现对心跳的监测和处理. 1 server端channel中加入心跳处理机制 // Id ...

  6. Netty心跳机制

    一.概念介绍网络中的接收和发送数据都是使用操作系统中的SOCKET进行实现.但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题.可是如何判断这个套接字是否还可以使用呢?这个就需要在系统 ...

  7. Netty 心跳服务之 IdleStateHandler 源码分析

    前言:Netty 提供的心跳介绍 Netty 作为一个网络框架,提供了诸多功能,比如我们之前说的编解码,Netty 准备很多现成的编解码器,同时,Netty 还为我们准备了网络中,非常重要的一个服务- ...

  8. Netty心跳简单Demo

    前面简单地了解了一下IdleStateHandler,我们现在写一个简单的心跳demo: 1)服务器端每隔5秒检测服务器端的读超时,如果5秒没有接受到客户端的写请求,也就说服务器端5秒没有收到读事件, ...

  9. Netty心跳之IdleStateHandler

    Netty提供了对心跳机制的天然支持,心跳可以检测远程端是否存活,或者活跃 今天我们就一起初识一下Netty4的心跳机制 Netty4.0提供了一个类,名为IdleStateHandler,这个类可以 ...

  10. netty心跳机制和断线重连(四)

    心跳是为了保证客户端和服务端的通信可用.因为各种原因客户端和服务端不能及时响应和接收信息.比如网络断开,停电 或者是客户端/服务端 高负载. 所以每隔一段时间 客户端发送心跳包到客户端  服务端做出心 ...

随机推荐

  1. 【Redis】Redis 持久化之 RDB 与 AOF 详解

    一.Redis 持久化 我们知道Redis的数据是全部存储在内存中的,如果机器突然GG,那么数据就会全部丢失,因此需要有持久化机制来保证数据不会一位宕机而丢失.Redis 为我们提供了两种持久化方案, ...

  2. 简单red5+obs推流实现直播系统开发,具体设置介绍

    前言:随便搞搞,先放一张效果图,

  3. WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一)

    WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一) 很多小伙伴在web开发中都遇倒的问题? JavaWeb项目注销后,可能存在通过浏览器缓存回退的方式进入主页系统 ​​ WEB项目 ...

  4. Docker(33)- 如何修改 docker 容器的端口映射

    如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 问题背景 docker run ...

  5. gcc入门(上)

    gcc:GNU Compiler Collection的缩写.最初是作为C语言的编译器,现在已支持多种语言.Gcc支持多种硬件平台.gcc是一个可移植跨平台编译器gcc还能跨平台交叉编译器.gcc有多 ...

  6. centos下多网卡做bond脚本

    多网卡或者单网卡形式下的网卡bonding #! /bin/sh #获取当前网卡数 ethnum=`lspci | grep Ethernet | wc -l` echo $ethnum #如果网卡数 ...

  7. C#委托的详细总结

    1.什么是委托 委托是C#中一种类型,它的作用相当于C语言中的函数指针,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用if- ...

  8. 讲武德,你们要的高性能日志工具 Log4j2,来了

    Log4j 介绍过了,SLF4J 介绍过了,Logback 也介绍过了,你以为日志系列的文章就到此终结了? 不不不,我告诉你,还有一个 Log4j 2,顾名思义,它就是 Log4j 的升级版,就好像手 ...

  9. vue路由懒加载方式

    方式一:结合Vue的异步组件和Webpack的代码分析 const Home = resole => {require.ensure(['../components/Home.vue'],() ...

  10. MSSQL渗透测试

    mssql-getshell 来源:独自等待,知乎,github xp_cmdshell 第一种:在SQL Server 2005之前版本中,xp_cmdshell是默认开启的,因此可以直接利用,执行 ...