Netty 断线重连解决方案
http://www.spring4all.com/article/889
本篇文章是Netty专题的第七篇,前面六篇文章如下:
- 高性能NIO框架Netty入门篇
- 高性能NIO框架Netty-对象传输
- 高性能NIO框架Netty-整合kryo高性能数据传输
- 高性能NIO框架Netty-整合Protobuf高性能数据传输
- Netty4自带编解码器详解
- Netty粘包拆包解决方案
用Netty实现长连接服务,当发生下面的情况时,会发生断线的情况。
- 网络问题
- 客户端启动时服务端挂掉了,连接不上服务端
- 客户端已经连接服务端,服务端突然挂掉了
- 其它问题等...
##如何解决上面的问题?
1.心跳机制检测连接存活
长连接是指建立的连接长期保持,不管有无数据包的发送都要保持连接通畅。心跳是用来检测一个系统是否存活或者网络链路是否通畅的一种方式,一般的做法是客户端定时向服务端发送心跳包,服务端收到心跳包后进行回复,客户端收到回复说明服务端存活。
通过心跳检测机制,可以检测客户端与服务的长连接是否保持,当客户端发送的心跳包没有收到服务端的响应式,可以认为服务端已经出故障了,这个时候可以重新连接或者选择其他的可用的服务进行连接。
在Netty中提供了一个IdleStateHandler类用于心跳检测,用法如下:
ch.pipeline().addLast("ping", new IdleStateHandler(60, 20, 60 * 10, TimeUnit.SECONDS));
- 第一个参数 60 表示读操作空闲时间
- 第二个参数 20 表示写操作空闲时间
- 第三个参数 60*10 表示读写操作空闲时间
- 第四个参数 单位/秒
在处理数据的ClientPoHandlerProto中增加userEventTriggered用来接收心跳检测结果,event.state()的状态分别对应上面三个参数的时间设置,当满足某个时间的条件时会触发事件。
public class ClientPoHandlerProto extends ChannelInboundHandlerAdapter {
private ImConnection imConnection = new ImConnection();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
MessageProto.Message message = (MessageProto.Message) msg;
System.out.println("client:" + message.getContent());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state().equals(IdleState.READER_IDLE)) {
System.out.println("长期没收到服务器推送数据");
//可以选择重新连接
} else if (event.state().equals(IdleState.WRITER_IDLE)) {
System.out.println("长期未向服务器发送数据");
//发送心跳包
ctx.writeAndFlush(MessageProto.Message.newBuilder().setType(1));
} else if (event.state().equals(IdleState.ALL_IDLE)) {
System.out.println("ALL");
}
}
}
}
服务端收到客户端发送的心跳消息后,回复一条信息
public class ServerPoHandlerProto extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
MessageProto.Message message = (MessageProto.Message) msg;
if (ConnectionPool.getChannel(message.getId()) == null) {
ConnectionPool.putChannel(message.getId(), ctx);
}
System.err.println("server:" + message.getId());
// ping
if (message.getType() == 1) {
ctx.writeAndFlush(message);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
当客户端20秒没往服务端发送过数据,就会触发IdleState.WRITER_IDLE事件,这个时候我们就像服务端发送一条心跳数据,跟业务无关,只是心跳。服务端收到心跳之后就会回复一条消息,表示已经收到了心跳的消息,只要收到了服务端回复的消息,那么就不会触发IdleState.READER_IDLE事件,如果触发了IdleState.READER_IDLE事件就说明服务端没有给客户端响应,这个时候可以选择重新连接。
2.启动时连接重试
在Netty中实现重连的操作比较简单,Netty已经封装好了,我们只需要稍微扩展一下即可。
连接的操作是客户端这边执行的,重连的逻辑也得加在客户端,首先我们来看启动时要是连接不上怎么去重试
增加一个负责重试逻辑的监听器,代码如下:
import java.util.concurrent.TimeUnit;
import com.netty.im.client.ImClientApp;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoop;
/**
* 负责监听启动时连接失败,重新连接功能
* @author yinjihuan
*
*/
public class ConnectionListener implements ChannelFutureListener {
private ImConnection imConnection = new ImConnection();
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (!channelFuture.isSuccess()) {
final EventLoop loop = channelFuture.channel().eventLoop();
loop.schedule(new Runnable() {
@Override
public void run() {
System.err.println("服务端链接不上,开始重连操作...");
imConnection.connect(ImClientApp.HOST, ImClientApp.PORT);
}
}, 1L, TimeUnit.SECONDS);
} else {
System.err.println("服务端链接成功...");
}
}
}
通过channelFuture.isSuccess()可以知道在连接的时候是成功了还是失败了,如果失败了我们就启动一个单独的线程来执行重新连接的操作。
只需要在ConnectionListener添加到ChannelFuture中去即可使用
public class ImConnection {
private Channel channel;
public Channel connect(String host, int port) {
doConnect(host, port);
return this.channel;
}
private void doConnect(String host, int port) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// 实体类传输数据,protobuf序列化
ch.pipeline().addLast("decoder",
new ProtobufDecoder(MessageProto.Message.getDefaultInstance()));
ch.pipeline().addLast("encoder",
new ProtobufEncoder());
ch.pipeline().addLast(new ClientPoHandlerProto());
}
});
ChannelFuture f = b.connect(host, port);
f.addListener(new ConnectionListener());
channel = f.channel();
} catch(Exception e) {
e.printStackTrace();
}
}
}
可以按照如下步骤进行测试:
- 直接启动客户端,不启动服务端
- 当连接失败的时候会进入ConnectionListener中的operationComplete方法执行我们的重连逻辑
3.运行中连接断开时重试
使用的过程中服务端突然挂了,就得用另一种方式来重连了,可以在处理数据的Handler中进行处理。
public class ClientPoHandlerProto extends ChannelInboundHandlerAdapter {
private ImConnection imConnection = new ImConnection();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
MessageProto.Message message = (MessageProto.Message) msg;
System.out.println("client:" + message.getContent());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.err.println("掉线了...");
//使用过程中断线重连
final EventLoop eventLoop = ctx.channel().eventLoop();
eventLoop.schedule(new Runnable() {
@Override
public void run() {
imConnection.connect(ImClientApp.HOST, ImClientApp.PORT);
}
}, 1L, TimeUnit.SECONDS);
super.channelInactive(ctx);
}
}
在连接断开时都会触发 channelInactive 方法, 处理重连的逻辑跟上面的一样。
可以按照如下步骤进行测试:
- 启动服务端
- 启动客户端,连接成功
- 停掉服务端就会触发channelInactive进行重连操作
源码参考:https://github.com/yinjihuan/netty-im
Netty 断线重连解决方案的更多相关文章
- Netty断线重连
Netty断线重连 最近使用Netty开发一个中转服务,需要一直保持与Server端的连接,网络中断后需要可以自动重连,查询官网资料,实现方案很简单,核心思想是在channelUnregistered ...
- Netty学习篇④-心跳机制及断线重连
心跳检测 前言 客户端和服务端的连接属于socket连接,也属于长连接,往往会存在客户端在连接了服务端之后就没有任何操作了,但还是占用了一个连接:当越来越多类似的客户端出现就会浪费很多连接,netty ...
- Netty 如何实现心跳机制与断线重连?
作者:sprinkle_liz www.jianshu.com/p/1a28e48edd92 心跳机制 何为心跳 所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, ...
- Netty 客户端断线重连
client 关闭后会执行 finally 代码块,可以在这里可以进行重连操作 public class NettyClient implements Runnable { private final ...
- Netty(六):Netty中的连接管理(心跳机制和定时断线重连)
何为心跳 顾名思义, 所谓心跳, 即在TCP长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可靠性, 有可 ...
- Netty — 心跳检测和断线重连
一.前言 由于在通信层的网络连接的不可靠性,比如:网络闪断,网络抖动等,经常会出现连接断开.这样对于使用长连接的应用而言,当突然高流量冲击势必会造成进行网络连接,从而产生网络堵塞,应用响应速度下降,延 ...
- netty的断线重连问题
手里的这个项目需要作为客户端,不断的接收服务端发来的数据,用的netty框架,但是一直存在一个问题,就是断线重连问题. 什么是断线重连呢? 就是我们这个客户端要保证一直与服务端保持连接,这样客户端才能 ...
- netty4 断线重连
转载:http://www.tuicool.com/articles/B7RzMbY 一 实现心跳检测 原理:当服务端每隔一段时间就会向客户端发送心跳包,客户端收到心跳包后同样也会回一个心跳包给服务端 ...
- ADOConnection断线重连
问题: ADOConnection断线重连问题描述: 使用ADOConnection连接oracle数据库,开始正常,当网络断开时数据库连接失败(此时查询ADOConnection.connected ...
随机推荐
- [POJ] Palindrome
Palindrome Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 62102 Accepted: 21643 Desc ...
- golang代码覆盖率
写了自动化,就得统计代码覆盖率= = 原链接:https://www.elastic.co/blog/code-coverage-for-your-golang-system-tests 其实看了原链 ...
- Android SQLite最简单demo实现(增删查改)
本来不太想写这篇博客的,但是看到网上的关于android数据库操作的博文都讲得很详细,对于像我这样的新手入门了解SQLite的基本操作有一定难度,所以我参考了网上的一些博客文章,并自己亲自摸索了一遍, ...
- 用django实现redirect的几种方法总结
用django开发web应用, 经常会遇到从一个旧的url转向一个新的url.这种隐射也许有规则,也许没有.但都是为了实现业务的需要.总体说来,有如下几种方法实现 django的 redirect.1 ...
- oracle按照时间过滤
select * from uc.uc_customer a where to_char(a.create_date,'YYYY-MM-DD')>'2017-07-21'
- kubenetes dns
E0228 07:32:28.912833 1 reflector.go:201] k8s.io/dns/pkg/dns/dns.go:147: Failed to list *v1.En ...
- 201671010140. 2016-2017-2 《Java程序设计》java学习第十五周
java学习第十五周 Java的GUI界面设计,框架以及主要部件填充,归置,布局管理,在第十一章和第十二章进行了系统的学习,在这两章的知识奠基下,可以简单的构造一个GUI用户界面,在两周的学习后,可以 ...
- 判断当前Selection是否为prefab
[判断当前Selection是否为prefab] PrefabUtility.GetPrefabParent(target) == null && PrefabUtility.GetP ...
- 【BZOJ2038】小Z的袜子【莫队】
题意 给出包含n个数字的序列,和m个查询.每次查询问区间[l,r]中挑选出两个数字,大小相同的概率为多少. 分析 莫队的入门题吧.代码是非常好写,关键是时间复杂度的证明.O(n*sqrt(n)).我还 ...
- iOS判断字母、数字串
以下为NSString类的扩展方法,分别是判断字符串是否只是包含字母.是否只包含数字.是否只包含字母和数字: //字母 - (BOOL)cdm_isOnlyLetters { NSCharacterS ...