传统的心跳包设计,基本上是服务端和客户端同时维护 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. python爬虫01在Chrome浏览器抓包

    尽量不要用国产浏览器,很多是有后门的 chrome是首选 百度 按下F12 element标签下对应的HTML代码 点击Network,可以看到很多请求 HTTP请求的方式有好几种,GET,POST, ...

  2. 使用css控制table的cellspacing和cellpadding属性

    HTML默认的表格样式之间有间隙,每次为了解决这些问题,总要在table标签里添加cellspacing和cellpadding,你是否也很厌倦这样的写法, 那么有没有对应的CSS属性能达到相同的效果 ...

  3. Spring Boot 2.4.0 正式发布!全新的配置处理机制,拥抱云原生!

    2020年11月12日,Spring官方发布了Spring Boot 2.4.0 GA的公告. 在这个版本中增加了大量的新特性和改进,下面我们一起看看在这个重要版本中都有哪些值得关注的内容! 更新内容 ...

  4. 【18】进大厂必须掌握的面试题-15个Kafka面试

    1.什么是Kafka? Wikipedia将Kafka定义为"由 Scala编写的Apache软件基金会开发的开源消息代理项目 ,并且是一个分布式的发布-订阅消息系统. 特征 描述 高吞吐量 ...

  5. c++ 11字符串与string转换常用函数

    这里主要介绍一下string to int 其他方法与这个类似,可到头文件 <string> 中查看 @_Str 转换的字符串 @_Idx 转换的长度(位数) @_Base 进制 doub ...

  6. Go知识点记录

    1.go中 堆的实现:https://ieevee.com/tech/2018/01/29/go-heap.html#3-containerheap%E5%8F%AF%E4%BB%A5%E7%94%A ...

  7. 11Linux之软件包管理

    11Linux之软件包管理 目录 11Linux之软件包管理 11 软件包管理 11.1 软件包介绍 11.1.1 编程语言分类 11.1.2 三种安装包 11.2 rpm包管理 11.2.1 rpm ...

  8. 希捷powerchoice磁盘休眠功能arm打包

    官方只提供了x86下面的包,没有提供arm下面的包,而我们的arm机器是32位的,需要编译一个支持armhf的二进制文件,这个文件只需要一个即可,但是编译是整套编译的,并且我们需要选定指定的版本,关闭 ...

  9. Java POI 导出带有图片的word

    1. 引入maven ,具体可以上github看一下,这里做简单的说明,是一个大神封装了一下 官方提供的语法 文本语法是 {{Text}} 图片语法是{{@Image}} 其他的自己去看官方文档 &l ...

  10. android intent-filter 注册网页链接打开app

    如下实现注册m.hao123.com的链接: <intent-filter><category android:name="android.intent.category. ...