咱們在使用客戶端和服務器端鏈接的過程當中,可能會由於各類問題致使客戶端和服務器的鏈接發生中斷,遇到這種狀況,通常狀況下咱們須要使用監控程序去監聽客戶端和服務器端的鏈接,若是第一時間發現鏈接斷開了,就須要手動去重連。比較麻煩,今天給你們介紹一種netty中自動重連的方式。git
要使用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/
最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!