Netty disconnection and reconnection solution

http://www.spring4all.com/article/889

This article is the seventh of the Netty topic. The first six articles are as follows:

Using Netty to implement a long connection service, when the following conditions occur, a disconnection will occur.

  • Internet problem
  • The server hangs up when the client starts and cannot connect to the server
  • The client has connected to the server, and the server suddenly hangs up
  • Other questions etc...

##How to solve the above problem?

1. Heartbeat mechanism detects connection survival

A long connection means that the established connection is maintained for a long time, and the connection must be kept unobstructed regardless of whether there are data packets sent. Heartbeat is a way to detect whether a system is alive or whether the network link is unobstructed. The general practice is that the client sends a heartbeat packet to the server regularly, the server responds after receiving the heartbeat packet, and the client receives the reply description. The server survives.

Through the heartbeat detection mechanism, it can detect whether the long connection between the client and the service is maintained. When the heartbeat packet sent by the client does not receive the response from the server, it can be considered that the server has failed. At this time, you can reconnect or choose another available services to connect to.

An IdleStateHandler class is provided in Netty for heartbeat detection, which is used as follows:

ch.pipeline().addLast("ping", new IdleStateHandler(60, 20, 60 * 10, TimeUnit.SECONDS)); 
  • The first parameter 60 indicates the idle time of the read operation
  • The second parameter 20 represents the idle time for write operations
  • The third parameter 60*10 represents the idle time of read and write operations
  • Fourth parameter unit/second

Add userEventTriggered to the ClientPoHandlerProto that processes data to receive heartbeat detection results. The state of event.state() corresponds to the time settings of the above three parameters, and an event will be triggered when a certain time condition is met.

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"); } } } } 

After the server receives the heartbeat message sent by the client, it replies with a message

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(); } } 

When the client has not sent data to the server for 20 seconds, the IdleState.WRITER_IDLE event will be triggered. At this time, we are like the server sending a heartbeat data, which has nothing to do with the business, just the heartbeat. After the server receives the heartbeat, it will reply with a message, indicating that the heartbeat message has been received. As long as the message replied by the server is received, the IdleState.READER_IDLE event will not be triggered. If the IdleState.READER_IDLE event is triggered, it means that the server There is no response to the client, and you can choose to reconnect at this time.

2. Connection retry at startup

The operation of reconnection in Netty is relatively simple. Netty is already packaged, and we only need to expand it a little.

The connection operation is performed by the client side, and the reconnection logic must also be added to the client side. First, let's look at how to retry if the connection fails at startup.

Add a listener responsible for the retry logic, the code is as follows:

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("服务端链接成功..."); } } } 

Through channelFuture.isSuccess(), we can know whether the connection succeeded or failed. If it fails, we start a separate thread to perform the reconnection operation.

Just add ConnectionListener to ChannelFuture to use

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(); } } } 

You can test it as follows:

  • Start the client directly without starting the server
  • When the connection fails, it will enter the operationComplete method in the ConnectionListener to execute our reconnection logic

3. Retry when the connection is disconnected during operation

In the process of using, if the server suddenly hangs up, you have to use another way to reconnect, which can be processed in the Handler that processes the data.

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); } } 

The channelInactive method is triggered when the connection is disconnected, and the logic for handling reconnection is the same as above.

You can test it as follows:

  • Start the server
  • Start the client, the connection is successful
  • Stopping the server will trigger channelInactive to reconnect

Source code reference: https://github.com/yinjihuan/netty-im

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324872270&siteId=291194637