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

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

一、服务端

1.1 编解码及 Handler

  1. ...
  2. .childHandler(new ChannelInitializer<SocketChannel>() {
  3. @Override
  4. protected void initChannel(SocketChannel channel) throws Exception {
  5. channel.pipeline().addLast("ping", new IdleStateHandler(10, 5, 10));
  6. channel.pipeline().addLast("encoder", new NettyMessageEncoder());
  7. channel.pipeline().addLast("decoder", new NettyMessageDecoder());
  8. channel.pipeline().addLast("message", new MessageHandler());
  9. channel.pipeline().addLast("heartbeat", new HeartbeatHandler());
  10. }
  11. });
  12. ...

HeartbeatHandler 为 心跳处理 Handler

1.2 心跳处理 Handler

  1. public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
  2. private static final Logger LOGGER = LoggerFactory.getLogger(HeartbeatHandler.class);
  3. private final AttributeKey<Integer> counterAttr = AttributeKey.valueOf(ChannelSupervise.COUNTER_ATTR);;
  4. @Override
  5. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
  6. super.userEventTriggered(ctx, evt);
  7. if(evt instanceof IdleStateEvent) {
  8. IdleStateEvent idleStateEvent = (IdleStateEvent)evt;
  9. switch (idleStateEvent.state()) {
  10. case READER_IDLE:
  11. NettyMessage<String> nettyMessage = new NettyMessage<>();
  12. nettyMessage.setSessionId(0L);
  13. nettyMessage.setType(NettyMessageTypeEnum.HEARTBEAT);
  14. ctx.writeAndFlush(nettyMessage).addListener(future -> {
  15. // if(future.isSuccess()) {
  16. // ctx.channel().attr(counterAttr).set(0);
  17. // }else {
  18. Integer counter = ctx.channel().attr(counterAttr).get();
  19. counter = counter + 1;
  20. LOGGER.info(ctx.channel().id().asShortText() + ",发送心跳: " + counter);
  21. if(counter >= 3) {
  22. ctx.close();
  23. } else {
  24. ctx.channel().attr(counterAttr).set(counter);
  25. }
  26. // }
  27. });
  28. break;
  29. default:
  30. break;
  31. }
  32. }
  33. }
  34. @Override
  35. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  36. ctx.channel().attr(counterAttr).set(0);
  37. ChannelSupervise.addChannel(ctx.channel());
  38. }
  39. @Override
  40. public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  41. ChannelSupervise.removeChannel(ctx.channel());
  42. }
  43. @Override
  44. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  45. ctx.channel().attr(counterAttr).set(0);
  46. ctx.fireChannelRead(msg);
  47. }
  48. @Override
  49. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  50. LOGGER.error("断开连接", cause);
  51. ctx.close();
  52. }
  53. }

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

二、客户端

2.1 编解码及 Handler

  1. ...
  2. .handler(new ChannelInitializer<SocketChannel>() {
  3. @Override
  4. protected void initChannel(SocketChannel channel) throws Exception {
  5. channel.pipeline().addLast("ping", new IdleStateHandler(5, 5, 3));
  6. channel.pipeline().addLast("encoder", new NettyMessageEncoder());
  7. channel.pipeline().addLast("decoder", new NettyMessageDecoder());
  8. channel.pipeline().addLast("heartbeat", new HeartbeatHandler());
  9. channel.pipeline().addLast("logger", new LoggingHandler(LogLevel.INFO));
  10. }
  11. });
  12. ...

HeartbeatHandler 为 心跳处理 Handler

2.2 心跳处理 Handler

  1. public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
  2. private static final Logger LOGGER = LoggerFactory.getLogger(HeartbeatHandler.class);
  3. @Override
  4. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
  5. if(evt instanceof IdleStateEvent) {
  6. IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
  7. switch (idleStateEvent.state()){
  8. case WRITER_IDLE:
  9. LOGGER.info("发送心跳包");
  10. NettyMessage<String> nettyMessage = new NettyMessage<>();
  11. nettyMessage.setSessionId(0L);
  12. nettyMessage.setType(NettyMessageTypeEnum.HEARTBEAT);
  13. ctx.writeAndFlush(nettyMessage).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
  14. break;
  15. default:
  16. break;
  17. }
  18. }
  19. super.userEventTriggered(ctx, evt);
  20. }
  21. ...
  22. }

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

参考

  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. P4683 [IOI2008] Type Printer 打印机

    题意描述 [IOI2008] Type Printer 打印机 几百年前的 IOI 的题目还是很好的呀. 给你一个 诡异的 打印机,它只能用已有的字符来打印,而且必须每一个都用到.(这岂不是活字印刷术 ...

  2. 849. Maximize Distance to Closest Person ——weekly contest 87

    849. Maximize Distance to Closest Person 题目链接:https://leetcode.com/problems/maximize-distance-to-clo ...

  3. Spring中基于XML的声明式事务控制配置步骤

    1.配置事务管理器 2.配置事务的通知 此时,我们就需要导入事务的约束 tx名称空间和约束,同时也需要aop的 使用tx:advice标签配置事务通知 属性: id:给事务通知起一个唯一标识 tran ...

  4. Java中的日期

    Date类(java.util.Date) 时间原点:1970年1月1日 8点0分0秒. 创建日期对象: package blog; import java.util.Date; public cla ...

  5. Linux 基础命令及基本目录

    Linux 基础命令及基本目录 一.网卡 1.网卡配置文件路径 ​ /etc/sysconfig/network-scripts/ifcfg-eth0 配置文件: TYPE=Ethernet # 以太 ...

  6. Python_环境搭建_jupyterNotebook的使用

    # @ Author : Collin_PXY # 虚拟环境的创建及Jupyter Notebook的基本使用 # Anaconda 和 Jupter Notebook的使用: # 首先得需要安装 A ...

  7. 各大数据库Java数据源参数

    Sybase: driver=com.sybase.jdbc3.jdbc.SybDriver url=jdbc:sybase:Tds:172.22.12.212:5000/ctninfo user=s ...

  8. 程序员注意【自verycd.com的JavaAmg77 】

    展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告 走过的路,回忆起来是那么曲折,把自己的一些心得体会分享给程序员兄弟姐妹们,虽然时代在变化,但是很可能你也会走我已经做过的1 ...

  9. 2种方式(线程间通信/互斥锁)实现两个线程,一个线程打印1-52,另一个线程打印字母A-Z,打印顺序为12A34B56C......5152Z

    //2019/06/13 本周HT面试遇到的问题,答得不是很好,自己重新做一下.面试只需要写出线程间通信的方式,//我当时大致知道思路,因为之前看过马士兵老师的多线程视频,但是代码写出来估计编译都是报 ...

  10. Golang中的OO(面向对象)

    组合 > 继承 Go中的设计,以为继承的被诟病,所以Golang的设计团队在语言的设计时并没有采用经典的OO模式 那Golang中各个部分是怎么联系到一起的呢 type ReadWriter i ...