一.前言

由于在通信层的网络连接的不可靠性,比如:网络闪断,网络抖动等,经常会出现连接断开。这样对于使用长连接的应用而言,当突然高流量冲击势必会造成进行网络连接,从而产生网络堵塞,应用响应速度下降,延迟上升,用户体验较差。

在通信层的高可用设计中,需要保活长连接的网络,保证通信能够正常。一般有两种设计方式:

  1. 利用TCP提供的连接保活特性
  2. 应用层做连接保活

本文主要介绍使用netty时应用层如何做连接保活,提高应用的可用性。

### 二.TCP连接保活性的局限

TCP协议层面提供了KeepAlive的机制保证连接的活跃,但是其有很多劣势:

  • 该保活机制非TCP协议的标准,默认是关闭
  • 该机制依赖操作系统,需要进行系统级配置,不够灵活方便
  • 当应用底层传输协议变更时,将无法适用

由于以上的原因,绝大多数的框架、应用处理连接的保活性都是在应用层处理。目前的主流方案是心跳检测,断线重连

### 三.应用层保证连接的活跃性

1.心跳检测

心跳检测机制:客户端每隔一段时间发送PING消息给服务端,服务端接受到后回复PONG消息。客户端如果在一定时间内没有收到PONG响应,则认为连接断开,服务端如果在一定时间内没有收到来自客户端的PING请求,则认为连接已经断开。通过这种来回的PING-PONG消息机制侦测连接的活跃性。

netty本身也提供了IdleStateHandler用于检测连接闲置,该Handler可以检测连接未发生读写事件而触发相应事件。

首先编写客户端心跳检测的Handler:

/**
* 心跳检测:
* 1. client发送"PING"消息
*
* @author huaijin
*/
public class ClientHeartBeatHandler extends ChannelHandlerAdapter { /**
* PING消息
*/
private static final String PING = "0"; /**
* PONG消息
*/
private static final String PONG = "1"; /**
* 分隔符
*/
private static final String SPLIT = "$_"; /**
* 读取到服务端响应,如果是PONG响应,则打印。如果是非PONG响应,则传递至下一个Handler
*
* @param ctx 处理上下文
* @param msg 消息
* @throws Exception
* @author huaijin
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (PONG.equals(msg)) {
System.out.println("from heart bean: " + msg);
} else {
ctx.fireChannelRead(msg);
}
} /**
* 处理触发的事件,如果是{@link IdleStateEvent},则判断是读或者是写。如果是du,则断开连接;
* 如果是写,则发送PING消息
*
* @param ctx 处理上下文
* @param evt 事件
* @throws Exception
* @author huaijin
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
switch (idleStateEvent.state()) {
case WRITER_IDLE:
sendPing(ctx);
break;
case READER_IDLE:
System.out.println("client close connection.");
closeConnection(ctx);
break;
case ALL_IDLE:
closeConnection(ctx);
break;
default:
break;
}
}
} /**
* 发送PING消息
*
* @param ctx 上下文
* @author huaijin
*/
private void sendPing(ChannelHandlerContext ctx) {
System.out.println("send heart beat: " + PING);
ctx.writeAndFlush(Unpooled.copiedBuffer((PING + SPLIT).getBytes()));
} /**
* 关闭连接
*
* @param ctx
* @author huaijin
*/
private void closeConnection(ChannelHandlerContext ctx) {
ctx.disconnect();
ctx.close();
}
}

然后再编写服务单心跳检测Handler:

/**
* 心跳检测:
* 1. server端接受到"PING",返回"PONG"消息
*
* @author huaijin
*/
public class ServerHeartBeatHandler extends ChannelHandlerAdapter { /**
* PONG消息
*/
private static final String PONG = "1"; /**
* PING消息
*/
private static final String PING = "0"; /**
* 消息分隔符
*/
private static final String SPLIT = "$_"; /**
* 如果是PING消息,则相应PONG。如果非,则传递至下个Handler
*
* @param ctx 上下文
* @param msg 消息
* @throws Exception
* @author huaijin
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (PING.equals(msg)) {
System.out.println("from heart beat: " + msg);
sendPong(ctx);
} else {
ctx.fireChannelRead(msg);
}
} /**
* 处理触发事件,如果是读事件,则关闭连接
*
* @param ctx 上下文
* @param evt 事件
* @throws Exception
* @author huaijin
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
if (idleStateEvent.state() == READER_IDLE) {
System.out.println("server close connection.");
closeConnection(ctx);
}
}
} /**
* 发送PONG消息
*
* @param ctx 上下文
* @author huaijin
*/
private void sendPong(ChannelHandlerContext ctx) {
System.out.println("send heart bean: " + PONG);
ctx.writeAndFlush(Unpooled.copiedBuffer((PONG + SPLIT).getBytes()));
} /**
* 关闭连接
*
* @param ctx 上下文
* @author huaijin
*/
private void closeConnection(ChannelHandlerContext ctx) {
ctx.disconnect();
ctx.close();
} }

通过以上的ClientHeartbeatHandler和ServerHeartBeatHandler和netty本身提供的IdleStateHandler能够完成心跳检测。

Note:

但是IdleStateHandler中有未读和未写的事件设置,这里需要非常着重注意。客户端的为读时间最好设置为服务端的未写时间的两倍,服务端的未读时间最好设置为客户端的未写时间的两倍。

2.断线重连

当心跳检测发现连接断开后,为了保证通信层的可用性,仍然需要重新连接,保证通信的可靠。对于短线重连一般有两种设计方式比较常见:

  1. 通过额外的线程定时轮循所有的连接的活跃性,如果发现其中有死连接,则执行重连
  2. 监听连接上发送的断开事件,如果发送则执行重连操作

这里我们首先看下第一种实现方式,netty中当Bootstrap执行connect操作后,会获得ChannelFuture对象,在该对象上执行close事件的监听,如果发生了close则提交重连操作。

public void connect(int port, String host) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new IdleStateHandler(
10, 5, 10));
ch.pipeline().addLast(new ClientHeartBeatHandler());
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
// 监听channel上的close事件
f.channel().closeFuture().sync();
} finally {
// 提交重连操作
executor.execute(() -> {
try {
System.out.println("reconnection to: " + "127.0.0.1:8080");
connect(8080, "127.0.0.1");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
} public static void main(String[] args) throws InterruptedException {
new EchoClient().connect(8080, "127.0.0.1");
Thread.currentThread().join();
}

但是该种方式对于应用而言,需要每个连接都有重连的线程,这样对于资源消耗比较大。建议采用第二种情况,使用额外的单线程轮循所有的连接,检测其是否活跃。该种方式在开源框架中有应用。

 timerExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
reconnect();
}
}, SCHEDULE_INTERVAL_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.SECONDS);

使用Java的定时线程池,定时执行重连操作。在重连操作中将检测连接的活跃性,如果非活跃,则执行重连。

参考

fescar中心跳和重连的源码

浅析 Netty 实现心跳机制与断线重连

Netty权威指南-心跳检测机制和断连重连

Netty — 心跳检测和断线重连的更多相关文章

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

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

  2. netty心跳检测机制

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

  3. Netty学习篇④-心跳机制及断线重连

    心跳检测 前言 客户端和服务端的连接属于socket连接,也属于长连接,往往会存在客户端在连接了服务端之后就没有任何操作了,但还是占用了一个连接:当越来越多类似的客户端出现就会浪费很多连接,netty ...

  4. Netty 如何实现心跳机制与断线重连?

    作者:sprinkle_liz www.jianshu.com/p/1a28e48edd92 心跳机制 何为心跳 所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, ...

  5. webSocket使用心跳包实现断线重连

    首先new一个webscoket的连接 let noticeSocketLink = new WebSocket(‘webSocket的地址’) 这里是连接成功之后的操作 linkNoticeWebs ...

  6. 面试官:Netty心跳检测机制是什么,怎么自定义检测间隔时间?

    哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,昨天在地里干了一天的 ...

  7. RabbitMQ心跳检测与掉线重连

    1.RabbitMQListener,自定义消息监听器 using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using ...

  8. websocket-heartbeat-js心跳检测库正式发布

    前言: 两年前写了一篇websocket心跳的博客——初探和实现websocket心跳重连.  阅读量一直比较大,加上最近考虑写一个自己的npm包,因此就完成了一个websocket心跳的检测库.在这 ...

  9. Netty(六):Netty中的连接管理(心跳机制和定时断线重连)

    何为心跳 顾名思义, 所谓心跳, 即在TCP长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可靠性, 有可 ...

随机推荐

  1. python操作excel表

    1.新增表并添加数据: 2.给工作表添加表名称,给表数据添加格式: import xlsxwriterdatas=(['Rent',1000], ['Gas',100], ['fish','画画'], ...

  2. windowsServer---- 在iis 上安装网站

    1.找到信息服务IIS 管理器如图: 2.进入后进行配置 3.添加本地网站 配置网站 如果域名没有解析的话,可以在添加一个  端口用于测试 点击浏览就行查看 如果报错 解决:找到目录浏览,并启动 点击 ...

  3. Slickflow.NET 开源工作流引擎高级开发(六) -- WebTest 引擎接口模拟测试工具集

    前言:引擎组件的接口测试不光是程序测试人员使用,而且也是产品负责人员需要用到的功能,因为在每一步流转过程中,就会完整模拟实际用户发生的场景,也就容易排查具体是程序问题还是业务问题,从而快速定位问题,及 ...

  4. NetCore WebAPI开发探索

      一.创建项目 新建api项目: 建好之后,一个默认的控制器已经有了: 运行就可以直接访问get接口获取数据: 二.跨平台部署 部署方面,微软已经完善的很好了,基本上算是傻瓜式操作.项目右键选择发布 ...

  5. Linux-用户/分组相关以及处理密码遗忘

    一.用户创建 1.简单创建 useradd 用户名 2.指定目录创建用户 useradd -d 目录路径 用户名 //注意这一类的目录路径必须写当前所在文件夹的相对路径而不能直接写目录名称 3.指定用 ...

  6. Winform中封装DevExpress的MarqueeProgressBarComtrol实现弹窗式进度条效果

    场景 在Winform中实现弹窗式进度条 就是新建一个窗体,然后在窗体中加入进度条控件,然后在触发进度条的事件中将加载进度报告给 进度条控件. 注: 博客主页: https://blog.csdn.n ...

  7. iOS常用算法之单链表查找倒数第n个节点(图解)

    拿到题目, 首先要先了解链表数据结构, 如下图: 常规思路: 利用数组, 遍历整个单链表, 将每个节点装入数组中, 最终拿到数组根据索引(数组长度-1-n)就得到了倒数第n个元素, 这里要注意从数组中 ...

  8. 当您解开后您从 Internet 上下载的压缩的文件时,文件的修改日期更改为您提取它的日期

    用鼠标右键单击该压缩的文件,然后单击属性. 单击常规选项卡,请单击取消阻止,然后单击确定. 从压缩文件中提取文件.

  9. 使用VS Code开发纸壳CMS自动编译主题压缩CSS,JS

    Visual Studio Code (简称 VS Code / VSC) 是一款免费开源的现代化轻量级代码编辑器,支持语法高亮.智能代码补全.自定义热键.括号匹配.代码片段.代码对比 Diff.GI ...

  10. coredump配置、产生、分析以及分析示例

    关键词:coredump.core_pattern.coredump_filter等等. 应用程序在运行过程中由于各种异常或者bug导致退出,在满足一定条件下产生一个core文件. 通常core文件包 ...