版权声明:版权声明:本文为博主原创文章,转载请标明出处! https://blog.csdn.net/qq_35457078/article/details/85317684
Netty客户端重连机制
场景:
1.Netty初次启动客户端,如果无法连接到服务端,将尝试重连。
2.在客户端与服务端保持长连接的过程中,如果连接断开,尝试与服务端重连。
-
主要代码
Netty客户端启动处理类
@Service("nettyClient") public class NettyClient { private final static Logger log = LoggerFactory.getLogger(NettyClient.class); private EventLoopGroup loop = new NioEventLoopGroup(); @Autowired private NettyFilter nettyFilter; @Autowired private AppConfig appConfig; /** * 重连标志 */ public static boolean retryConnectFlag = false; public void run() { doConnect(new Bootstrap(), loop); } /** * netty client 连接,连接失败10秒后重试连接 */ public void doConnect(Bootstrap bootstrap, EventLoopGroup eventLoopGroup) { ChannelFuture f = null; try { if (bootstrap != null) { bootstrap.group(eventLoopGroup); bootstrap.channel(NioSocketChannel.class); bootstrap.option(ChannelOption.SO_KEEPALIVE, true); bootstrap.handler(nettyFilter); bootstrap.remoteAddress(appConfig.getHost(), appConfig.getPort()); bootstrap.connect().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { // TODO Auto-generated method stub } }); // Cache.channel为缓存的与服务端的连接channel,在其他发送消息的时候使用。 Cache.channel = bootstrap.connect().addListener((ChannelFuture futureListener) -> { final EventLoop eventLoop = futureListener.channel().eventLoop(); if (!futureListener.isSuccess()) { log.warn("客户端与服务端建立连接失败,10s之后尝试重连..."); eventLoop.schedule(() -> doConnect(new Bootstrap(), eventLoop), 10, TimeUnit.SECONDS); } else { if (retryConnectFlag) { // 如果连接成功后,再次断开,则尝试重连。 retryConnectFlag = false; log.info("客户端重连成功,port:{}", appConfig.getPort()); } else { // 客户端首次成功连接服务端后,这里有个验证登录服务端的动作(不要验证可以取消) log.info("客户端与服务器连接成功,port:{},开始登录服务端...", appConfig.getPort()); } } }).channel(); } } catch (Exception e) { log.error("连接客户端失败,失败信息:" + e); }finally { // 要实现重连,所以不能关闭loop //loop.shutdownGracefully(); } } }
客户端与服务端心跳处理类,与客户端断开连接后的重试代码在channelInactive()这个方法中
@Service("idleclienthandler") @ChannelHandler.Sharable public class IdleClientHandler extends SimpleChannelInboundHandler<MessageBase> { private final static Logger logger = LoggerFactory.getLogger(IdleClientHandler.class); private long heartbeatCount = 0; @Autowired private NettyClient nettyClient; @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; logger.info(ctx.channel().remoteAddress() + ",超时类型:" + event.state()); sendPingMsg(ctx); } else { super.userEventTriggered(ctx, evt); } } /** * 发送ping消息 * @param context */ protected void sendPingMsg(ChannelHandlerContext context) { MessageBase body = NettyMessage.MessageBase.newBuilder() .setCmd(CommandType.PING) .setPtcode(Cache.ptcode) .setData("ping") .build(); context.writeAndFlush(body); heartbeatCount++; logger.info("Client sent ping msg to " + context.channel().remoteAddress() + ", count: " + heartbeatCount); } /** * 处理断开重连 */ @SuppressWarnings("static-access") @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.info("与服务器连接断开,尝试重新连接..."); nettyClient.retryConnectFlag = true; final EventLoop eventLoop = ctx.channel().eventLoop(); nettyClient.doConnect(new Bootstrap(), eventLoop); super.channelInactive(ctx); } @Override protected void channelRead0(ChannelHandlerContext ctx, MessageBase msg) throws Exception { logger.info("收到的消息!{}",msg); } }