netty系列之:自动重连

简介

咱们在使用客户端和服务器端链接的过程当中,可能会由于各类问题致使客户端和服务器的链接发生中断,遇到这种状况,通常状况下咱们须要使用监控程序去监听客户端和服务器端的链接,若是第一时间发现链接断开了,就须要手动去重连。比较麻烦,今天给你们介绍一种netty中自动重连的方式。git

使用netty创建链接

要使用netty创建链接,首先须要启动服务器,一般来讲服务器经过使用ServerBootstrap来启动服务器,以下所示:github

// 绑定端口并启动
ChannelFuture f = b.bind(PORT).sync();

对于客户端来讲,能够经过Bootstrap按以下的方式启动:segmentfault

// 链接服务器
ChannelFuture f = b.connect(HOST, PORT).sync();

自动重链接的原理

那么当客户端和服务器端的链接断了以后,如何自动重连呢?服务器

对于客户端来讲,自动重连只须要再次调用Bootstrap的connect方法便可。如今的关键问题在于,如何找到从新调用connect的时机。ide

咱们知道,不论server仍是client,对于消息的处理都须要注册专门处理消息的handler。oop

对于读取消息来讲,通常须要继承ChannelInboundHandlerAdapter,在这个handler中定义了不少和channel生命周期有关的方法,咱们能够从这些生命周期的方法入手。url

通常来讲客户端和服务器链接的状态是这的:日志

CHANNEL REGISTERED--》CHANNEL ACTIVE --》 READ --》READ COMPLETE --》 CHANNEL INACTIVE --》 CHANNEL UNREGISTERED netty

客户端和服务器端的链接若是关闭的话,则会触发CHANNEL INACTIVE 和 CHANNEL UNREGISTERED 两个事件,这样咱们在客户端重写下面两个方法,在方法中加入重连的逻辑便可。code

@Override
    public void channelInactive(final ChannelHandlerContext ctx) {
        println("链接断开:" + ctx.channel().remoteAddress());
    }

    @Override
    public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception {
        println("sleep:" + ReconnectClient.RECONNECT_DELAY + 's');

        ctx.channel().eventLoop().schedule(() -> {
            println("重链接: " + ReconnectClient.HOST + ':' + ReconnectClient.PORT);
            ReconnectClient.connect();
        }, ReconnectClient.RECONNECT_DELAY, TimeUnit.SECONDS);
    }

在channelInactive方法中,咱们只是打印了一些日志。主要逻辑在channelUnregistered方法中,在这个方法中咱们首先经过ctx获取到当前的channel,而后拿到channel中的eventLoop,而后调用它的schedule方法,在给定的时间后从新调用connect()方法。

connect()方法返回的是一个ChannelFuture,因此能够在ChannelFuture中添加一些listener用来监听connect的执行状态。

这里定义的connect方法以下:

static void connect() {
        bs.connect().addListener(future -> {
            if (future.cause() != null) {
                handler.startTime = -1;
                handler.println("创建链接失败: " + future.cause());
            }
        });
    }

模拟自动重连

上一节咱们已经知道怎么自动重连了,本小节将会对自动重连进行一个模拟。

这里要介绍一个类,叫作IdleStateHandler,从名字就能够看出来这个类是当 Channel 没有作任何read, write操做的时候,就会触发这个Idle的状态。

表示Idle状态的类叫作IdleStateEvent,Idle有6个状态,分别是FIRST_READER_IDLE_STATE_EVENT,READER_IDLE_STATE_EVENT,FIRST_WRITER_IDLE_STATE_EVENT,WRITER_IDLE_STATE_EVENT,FIRST_ALL_IDLE_STATE_EVENT和ALL_IDLE_STATE_EVENT。

分别表示读取状态的IDLE,写状态的IDLE和读写状态的IDLE。

这样咱们在client启动的时候就能够加上IdleStateHandler,当client一段时间没有读取到server端发来的消息的时候,咱们就调用ctx.close()将channel关闭,从而出发client端的重连操做。

bs.group(group)
                .channel(NioSocketChannel.class)
                .remoteAddress(HOST, PORT)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new IdleStateHandler(READ_TIMEOUT, 0, 0), handler);
                    }
                });

IdleStateEvent是一个用户出发的event,要捕获到这个event,须要重写userEventTriggered:

public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (!(evt instanceof IdleStateEvent)) {
            return;
        }
        IdleStateEvent e = (IdleStateEvent) evt;
        if (e.state() == IdleState.READER_IDLE) {
            // 在Idle状态
            println("Idle状态,关闭链接");
            ctx.close();
        }
    }

上面的例子中,咱们捕获了IdleStateEvent,并判断若是IdleState的状态是IdleState.READER_IDLE,那么就将channel关闭。

总结

本文咱们介绍了重连的原理和用户触发的Event,但愿你们可以喜欢。

本文的例子能够参考:learn-netty4

本文已收录于 http://www.flydean.com/09-netty-reconnect/

最通俗的解读,最深入的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注个人公众号:「程序那些事」,懂技术,更懂你!