【Netty】断线重连 详解
前言:
在 日常生活 以及 企业应用 中,我们经常遇到一种情况 —— 长连接 场景下,因为 各种原因,使得 连接断开
就像我们玩 网络游戏 一样,可能有时候 “网不好”,就会看到页面上有个 “圈圈” 在转,这就是 断线自动重连 机制!
那么,使用 Netty
,如何 实现 断线自动重连
呢?
我们先来思考下,断线自动重连
需要在 哪些情况 下实现呢?
应用场景:
客户端启动 连接服务端 时
:
如果 网络 或 服务端 有问题,客户端连接失败,
可以 重连,重连逻辑 加在 客户端系统运行过程 中
:
网络故障 或 服务端故障,导致客户端与服务端 断开连接 了也 需要重连,
可以在 客户端 处理数据的 Handler 的 channelInactive()方法 中进行重连
接下来,本人就依据上述思想,来实现下 断线自动重连
机制:
代码实现:
首先是 服务端:
服务端:
package edu.youzg.demo.reconnect;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.CharsetUtil;
/**
* @Author: Youzg
* @CreateTime: 2021-05-07 08:54
* @Description: 带你深究Java的本质!
*/
public class NettyServerDemo {
private int port;
public NettyServerDemo(int port) {
this.port = port;
}
public static void main(String[] args) {
NettyServerDemo nettyServer = new NettyServerDemo(9000);
nettyServer.startUp();
}
private void startUp() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ReconnectServerHandler());
}
});
System.out.println("Netty Server start...");
ChannelFuture channelFuture = bootstrap.bind(this.port).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
class ReconnectServerHandler extends ChannelInboundHandlerAdapter {
/**
* 读取客户端发送的数据
*
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @param msg 就是客户端发送的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));
}
/**
* 数据读取完毕处理方法
*
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = Unpooled.copiedBuffer("Yes, Youzg is a cool man!".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
/**
* 处理异常, 一般是需要关闭通道
*
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @param cause 异常信息
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
}
接下来是 客户端:
客户端:
package edu.youzg.demo.reconnect;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil;
import java.util.concurrent.TimeUnit;
/**
* @Author: Youzg
* @CreateTime: 2021-05-07 09:02
* @Description: 带你深究Java的本质!
*/
public class NettyClientDemo {
private String ip;
private int port;
private Bootstrap bootstrap;
public NettyClientDemo(String ip, int port) {
this.ip = ip;
this.port = port;
initClient();
}
public static void main(String[] args) throws InterruptedException {
NettyClientDemo nettyClient = new NettyClientDemo("127.0.0.1", 9000);
nettyClient.connect();
}
public void connect() throws InterruptedException {
System.err.println("connect to Netty Server...");
ChannelFuture channelFuture = this.bootstrap.connect(ip, port);
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) { // 没有连接成功,进行 “重连”
future.channel().eventLoop().schedule(() -> {
System.err.println("Connection Netty Server failed, trying to reconnect...");
try {
connect();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 3000, TimeUnit.MILLISECONDS); // 设置 3s 进行一次 “重连”
} else {
System.out.println("Connect Netty Server successfully!");
}
}
});
// 关闭对 当前通道 的监听
channelFuture.channel().closeFuture().sync();
}
private void initClient() {
NioEventLoopGroup group = new NioEventLoopGroup();
this.bootstrap = new Bootstrap();
this.bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ReconnectClientHandler(NettyClientDemo.this));
}
});
}
class ReconnectClientHandler extends ChannelInboundHandlerAdapter {
private NettyClientDemo nettyClient;
public ReconnectClientHandler(NettyClientDemo nettyClient) {
this.nettyClient = nettyClient;
}
/**
* 当客户端连接服务器完成就会触发该方法
*
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = Unpooled.copiedBuffer("Yuozg is a good man!".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
/**
* 当通道有读取事件时会触发,即服务端发送数据给客户端
*
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @param msg 服务端消息
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("收到服务端的消息:" + buf.toString(CharsetUtil.UTF_8));
}
/**
* channel 处于不活动状态时调用
*
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.err.println("正在断线重连中...");
this.nettyClient.connect();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
}
最后,本人来展示下 运行结果:
运行结果:
客户端启动 连接服务端 时:
可以看到:
客户端启动 连接服务端 时,实现了 连接失败重连
系统运行过程 中
可以看到:
系统运行过程 中,实现了 断线自动重连
那么,至此,Netty
的 断线自动重连 机制,就实现了!