簡介
我們在使用用戶端和伺服器端連接配接的過程中,可能會因為各種問題導緻用戶端和伺服器的連接配接發生中斷,遇到這種情況,一般情況下我們需要使用監控程式去監聽用戶端和伺服器端的連接配接,如果第一時間發現連接配接斷開了,就需要手動去重連。比較麻煩,今天給大家介紹一種netty中自動重連的方式。
使用netty建立連接配接
要使用netty建立連接配接,首先需要啟動伺服器,通常來說伺服器通過使用ServerBootstrap來啟動伺服器,如下所示:
// 綁定端口并啟動
ChannelFuture f = b.bind(PORT).sync();
對于用戶端來說,可以通過Bootstrap按如下的方式啟動:
// 連接配接伺服器
ChannelFuture f = b.connect(HOST, PORT).sync();
自動重連接配接的原理
那麼當用戶端和伺服器端的連接配接斷了之後,如何自動重連呢?
對于用戶端來說,自動重連隻需要再次調用Bootstrap的connect方法即可。現在的關鍵問題在于,如何找到重新調用connect的時機。
我們知道,不論server還是client,對于消息的處理都需要注冊專門處理消息的handler。
對于讀取消息來說,一般需要繼承ChannelInboundHandlerAdapter,在這個handler中定義了很多和channel生命周期有關的方法,我們可以從這些生命周期的方法入手。
一般來說用戶端和伺服器連接配接的狀态是這的:
CHANNEL REGISTERED–》CHANNEL ACTIVE –》 READ –》READ COMPLETE –》 CHANNEL INACTIVE –》 CHANNEL UNREGISTERED
用戶端和伺服器端的連接配接如果關閉的話,則會觸發CHANNEL INACTIVE 和 CHANNEL UNREGISTERED 兩個事件,這樣我們在用戶端重寫下面兩個方法,在方法中加入重連的邏輯即可。
@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/最通俗的解讀,最深刻的幹貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
歡迎關注我的公衆号:「程式那些事」,懂技術,更懂你!