netty系列之:自動重連

簡介

我們在使用客戶端和服務(wù)器端連接的過程中,可能會因?yàn)楦鞣N問題導(dǎo)致客戶端和服務(wù)器的連接發(fā)生中斷,遇到這種情況,一般情況下我們需要使用監(jiān)控程序去監(jiān)聽客戶端和服務(wù)器端的連接,如果第一時(shí)間發(fā)現(xiàn)連接斷開了,就需要手動去重連。比較麻煩,今天給大家介紹一種netty中自動重連的方式。

使用netty建立連接

要使用netty建立連接,首先需要啟動服務(wù)器,通常來說服務(wù)器通過使用ServerBootstrap來啟動服務(wù)器,如下所示:

// 綁定端口并啟動
ChannelFuture f = b.bind(PORT).sync();

對于客戶端來說,可以通過Bootstrap按如下的方式啟動:

// 連接服務(wù)器
ChannelFuture f = b.connect(HOST, PORT).sync();

自動重連接的原理

那么當(dāng)客戶端和服務(wù)器端的連接斷了之后,如何自動重連呢?

對于客戶端來說,自動重連只需要再次調(diào)用Bootstrap的connect方法即可?,F(xiàn)在的關(guān)鍵問題在于,如何找到重新調(diào)用connect的時(shí)機(jī)。

我們知道,不論server還是client,對于消息的處理都需要注冊專門處理消息的handler。

對于讀取消息來說,一般需要繼承ChannelInboundHandlerAdapter,在這個(gè)handler中定義了很多和channel生命周期有關(guān)的方法,我們可以從這些生命周期的方法入手。

一般來說客戶端和服務(wù)器連接的狀態(tài)是這的:

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

客戶端和服務(wù)器端的連接如果關(guān)閉的話,則會觸發(fā)CHANNEL INACTIVE 和 CHANNEL UNREGISTERED 兩個(gè)事件,這樣我們在客戶端重寫下面兩個(gè)方法,在方法中加入重連的邏輯即可。

    @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方法中,在這個(gè)方法中我們首先通過ctx獲取到當(dāng)前的channel,然后拿到channel中的eventLoop,然后調(diào)用它的schedule方法,在給定的時(shí)間后重新調(diào)用connect()方法。

connect()方法返回的是一個(gè)ChannelFuture,所以可以在ChannelFuture中添加一些listener用來監(jiān)聽connect的執(zhí)行狀態(tài)。

這里定義的connect方法如下:

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

模擬自動重連

上一節(jié)我們已經(jīng)知道怎么自動重連了,本小節(jié)將會對自動重連進(jìn)行一個(gè)模擬。

這里要介紹一個(gè)類,叫做IdleStateHandler,從名字就可以看出來這個(gè)類是當(dāng) Channel 沒有做任何read, write操作的時(shí)候,就會觸發(fā)這個(gè)Idle的狀態(tài)。

表示Idle狀態(tài)的類叫做IdleStateEvent,Idle有6個(gè)狀態(tài),分別是FIRST_READER_IDLE_STATE_EVENT,READER_IDLE_STATE_EVENT,F(xiàn)IRST_WRITER_IDLE_STATE_EVENT,WRITER_IDLE_STATE_EVENT,F(xiàn)IRST_ALL_IDLE_STATE_EVENT和ALL_IDLE_STATE_EVENT。

分別表示讀取狀態(tài)的IDLE,寫狀態(tài)的IDLE和讀寫狀態(tài)的IDLE。

這樣我們在client啟動的時(shí)候就可以加上IdleStateHandler,當(dāng)client一段時(shí)間沒有讀取到server端發(fā)來的消息的時(shí)候,我們就調(diào)用ctx.close()將channel關(guān)閉,從而出發(fā)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是一個(gè)用戶出發(fā)的event,要捕獲到這個(gè)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狀態(tài)
            println("Idle狀態(tài),關(guān)閉連接");
            ctx.close();
        }
    }

上面的例子中,我們捕獲了IdleStateEvent,并判斷如果IdleState的狀態(tài)是IdleState.READER_IDLE,那么就將channel關(guān)閉。

總結(jié)

本文我們介紹了重連的原理和用戶觸發(fā)的Event,希望大家能夠喜歡。

本文的例子可以參考:learn-netty4

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

最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!

歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù),更懂你!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容