Nett中的心跳機(jī)制與斷線重連詳解
心跳機(jī)制
我們以客戶(hù)端發(fā)送心跳為例,平時(shí)我們的心跳實(shí)現(xiàn)方式可能是搞個(gè)定時(shí)器,定時(shí)發(fā)送是吧,但是在Netty中卻不一樣,心跳被稱(chēng)為空閑檢測(cè),因?yàn)樾奶淖钪饕饔镁褪桥袛嗍欠翊婊?、是否假死等情況,所以Netty中并不是定時(shí)發(fā)送,而是空閑的情況下才發(fā)送,空閑指的是一段時(shí)間內(nèi)無(wú)讀寫(xiě)事件的發(fā)生,也就是沒(méi)有信息接收和信息發(fā)送啦,這時(shí)候我就要檢測(cè)一下了
所以Netty的機(jī)制就是通過(guò)監(jiān)測(cè)一段時(shí)間內(nèi)是否有信息的讀、寫(xiě)發(fā)生,然后產(chǎn)生一個(gè)事件,我們可以處理這個(gè)事件來(lái)達(dá)到我們的目的
IdleStateHandler
心跳與其他的數(shù)據(jù)處理Handler一樣,都是建立連接后的數(shù)據(jù)處理,所以同樣都是處理管道中的,IdleStateHandler就是Netty幫我們封裝好了的處理類(lèi),我們可以直接使用并將它放入管道中,就可以自動(dòng)檢測(cè)一段時(shí)間內(nèi)是否有讀、寫(xiě)發(fā)生了,常用的構(gòu)造有4個(gè)參數(shù):
- readerIdleTime:讀事件檢測(cè),該時(shí)間內(nèi)沒(méi)有信息讀取就會(huì)產(chǎn)生一個(gè)讀信息的事件
- writerIdleTime:寫(xiě)事件檢測(cè),該時(shí)間內(nèi)沒(méi)有信息發(fā)送就會(huì)產(chǎn)生一個(gè)寫(xiě)信息的事件
- allIdleTime:讀寫(xiě)事件檢測(cè),該時(shí)間內(nèi)沒(méi)有信息發(fā)送或者讀取就會(huì)產(chǎn)生一個(gè)讀寫(xiě)信息的事件
- unit:時(shí)間單位
public IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) { this(false, readerIdleTime, writerIdleTime, allIdleTime, unit); }
使用:
// 管道中直接添加 // 時(shí)間配置為0則代表不檢測(cè)對(duì)應(yīng)事件 channel.pipeline().addLast(new IdleStateHandler(5,0,0, TimeUnit.SECONDS))
觸發(fā)的事件類(lèi)型:
- IdleState.READER_IDLE:讀信息的事件
- IdleState.WRITER_IDLE:寫(xiě)信息的事件
- IdleState.ALL_IDLE:讀寫(xiě)信息的事件
舉個(gè)例子:假設(shè)我們是客戶(hù)端主動(dòng)發(fā)送心跳,那客戶(hù)端就要檢測(cè)一段時(shí)間內(nèi)是否有寫(xiě)信息的操作,如果沒(méi)有就會(huì)觸發(fā)IdleState.WRITER_IDLE事件,那我們監(jiān)聽(tīng)這個(gè)事件,就做出自己的處理,那就是主動(dòng)發(fā)送心跳包嘛
下面我們就以客戶(hù)端發(fā)送心跳包為例!
客戶(hù)端
像上面那樣直接使用IdleStateHandler,那還需要單獨(dú)寫(xiě)一個(gè)事件處理的類(lèi),所以這里我們直接繼承IdleStateHandler
ClientHeartbeatHandler 如下:
我們?cè)O(shè)置5秒,5秒內(nèi)沒(méi)有寫(xiě)過(guò)信息,我們就發(fā)送心跳包(這里消息很隨意,圖方便)
@Slf4j public class ClientHeartbeatHandler extends IdleStateHandler { // 設(shè)置寫(xiě)事件為5s // 如果5s沒(méi)有寫(xiě)事件發(fā)生 就會(huì)觸發(fā)下面的IdleStateEvent private static final int WRITE_IDLE_TIME = 5; public ClientHeartbeatHandler() { super(0, WRITE_IDLE_TIME, 0, TimeUnit.SECONDS); } @Override protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { // 指定時(shí)間內(nèi)沒(méi)有寫(xiě)事件發(fā)送 就會(huì)觸發(fā) IdleState.WRITER_IDLE 類(lèi)型事件 // 我們就可以對(duì)該連接進(jìn)行處理 主動(dòng)發(fā)送心跳 if(evt.state()== IdleState.WRITER_IDLE){ log.info("{} 秒內(nèi)沒(méi)有發(fā)送數(shù)據(jù),再不發(fā)送小心和服務(wù)端斷開(kāi)連接", WRITE_IDLE_TIME); ctx.writeAndFlush(new NettyMsg(ServiceCodeEnum.TEST_TYPE,"我還活著,不要斷開(kāi)連接")); } } }
NettyClient
管道中加入這個(gè)即可,注意要放在編解碼之后
服務(wù)端
基本跟客戶(hù)端一樣,但是由于是客戶(hù)端給我發(fā)心跳,所以服務(wù)端要監(jiān)測(cè)讀事件
ServerHeartbeatHandler
這里我們?cè)O(shè)置10s,一般會(huì)更多因?yàn)橐o客戶(hù)端一些容忍度,適量就好,一旦沒(méi)滿足就和客戶(hù)端斷開(kāi)連接
@Slf4j public class ServerHeartbeatHandler extends IdleStateHandler { // 設(shè)置讀取事件為10s // 如果10s沒(méi)有讀事件發(fā)生 就會(huì)觸發(fā)下面的IdleStateEvent private static final int READER_IDLE_TIME = 10; public ServerHeartbeatHandler() { super(READER_IDLE_TIME, 0, 0, TimeUnit.SECONDS); } @Override protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { // 指定時(shí)間內(nèi)沒(méi)有讀事件發(fā)送 就會(huì)觸發(fā) IdleState.READER_IDLE 類(lèi)型事件 // 我們就可以對(duì)該連接進(jìn)行處理 這里是直接關(guān)閉 if(evt.state()== IdleState.READER_IDLE){ log.info("{} 秒內(nèi)沒(méi)有讀取到數(shù)據(jù),關(guān)閉連接", READER_IDLE_TIME); ctx.channel().close(); } } }
NettyServer
測(cè)試
正常情況
服務(wù)端
客戶(hù)端
異常情況
我們將客戶(hù)端心跳拉長(zhǎng)
可以發(fā)現(xiàn)時(shí)間一到自動(dòng)斷連
總結(jié)
這里是以客戶(hù)端發(fā)送心跳為例,如果是以服務(wù)端發(fā)送心跳也是一樣,主要就是對(duì)讀寫(xiě)事件的監(jiān)聽(tīng),然后做出相應(yīng)處理,上面其實(shí)是很隨意的哈,正常情況下心跳包是會(huì)用專(zhuān)門(mén)的消息類(lèi)型的,而且服務(wù)端也需要做出應(yīng)答的,心跳應(yīng)答也會(huì)有對(duì)應(yīng)的消息類(lèi)型
斷線重連
這個(gè)就很簡(jiǎn)單了,肯定是斷線后,客戶(hù)端發(fā)起重連嘛,就是在客戶(hù)端檢測(cè)到連接斷開(kāi)的時(shí)候,重新連接就好了
ClientHeartbeatHandler加上一段:
看看效果,客戶(hù)端心跳時(shí)間還是12s哈
能這么隨意嗎?當(dāng)然不行啊,別學(xué)我,正常會(huì)搞多次重連,而且謹(jǐn)慎起見(jiàn),每次重連還可以加一個(gè)時(shí)間的間隔,可以學(xué)一下nacos里面的高級(jí)玩法,隨著重連次數(shù)的增加,間隔重試的時(shí)間也隨之增加,比如:
第一次重連是4s,第二次重連是8s,第三次是16s,以此類(lèi)推
也就是設(shè)置重連間隔時(shí)間基數(shù)為4s,每嘗試一次,左移一位(4<<1),連接成功后基數(shù)復(fù)位為4s
需要設(shè)置最大重試次數(shù),最長(zhǎng)間隔時(shí)間(一直左移后面會(huì)很長(zhǎng)時(shí)間,所以要設(shè)置一個(gè)上限,超過(guò)了就以上限為準(zhǔn))
偽代碼如下:
@Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { log.info(ctx.channel().remoteAddress() + " 已斷開(kāi)連接,開(kāi)始重連"); int maxRetryNum=6; //最大重連次數(shù) int maxRetryTime=60; // 最大重連時(shí)間 int baseRetryTime=4; // 間隔時(shí)間基數(shù) // 開(kāi)始重連 for (int i = 1; i <= maxRetryNum; i++) { log.info("第{}次重連,間隔{}s后開(kāi)始",i,baseRetryTime > maxRetryTime ? maxRetryTime:baseRetryTime); ctx.channel().eventLoop().schedule(()->{ // 重連操作 // ....... // 判斷是否超過(guò)隔間上限 },baseRetryTime > maxRetryTime ? maxRetryTime:baseRetryTime,TimeUnit.SECONDS); // 左移擴(kuò)大一倍 baseRetryTime = baseRetryTime << 1; } log.info("這么多次重連都不行寄了吧"); }
偽代碼哈,偽代碼!
結(jié)果如下:
建立連接后,手動(dòng)關(guān)閉服務(wù)端,可以看到重連了6次,每次間隔時(shí)間翻倍,達(dá)到間隔時(shí)間上限后,就以上限的時(shí)間為準(zhǔn)
到此這篇關(guān)于Nett中的心跳機(jī)制與斷線重連詳解的文章就介紹到這了,更多相關(guān)Nett心跳機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java調(diào)用騰訊云短信API接口的實(shí)現(xiàn)
這篇文章主要介紹了Java調(diào)用騰訊云短信API接口的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Spring常見(jiàn)的事務(wù)失效場(chǎng)景及解決方案
Spring 事務(wù)管理是企業(yè)級(jí)應(yīng)用開(kāi)發(fā)中不可或缺的一部分,它可以幫助我們確保數(shù)據(jù)的一致性和完整性,然而,在實(shí)際開(kāi)發(fā)中,由于各種原因,事務(wù)可能會(huì)失效,本文將詳細(xì)介紹 Spring 事務(wù)失效的常見(jiàn)情況,并提供相應(yīng)的解決方案和示例代碼,需要的朋友可以參考下2024-11-11Java同步鎖synchronized用法的最全總結(jié)
這篇文章主要介紹了Java同步鎖synchronized用法的最全總結(jié),需要的朋友可以參考下,文章詳細(xì)講解了Java同步鎖Synchronized的使用方法和需要注意的點(diǎn),希望對(duì)你有所幫助2023-03-03SpringCloud之注冊(cè)中心之Nacos負(fù)載均衡詳解
Nacos提供多種負(fù)載均衡策略,包括權(quán)重、同機(jī)房、同地域、同環(huán)境等,服務(wù)下線和權(quán)重配置可以通過(guò)Nacos管理界面進(jìn)行,同時(shí),Nacos使用Raft算法選舉Leader節(jié)點(diǎn),若IP地址改變可能會(huì)影響Leader選舉,配置同集群優(yōu)先訪問(wèn)可以提高訪問(wèn)速度,通過(guò)配置集群名稱(chēng)和負(fù)載均衡策略2025-03-03Java 在PPT中創(chuàng)建散點(diǎn)圖的實(shí)現(xiàn)示例
本文將以Java代碼示例展示如何在PPT幻燈片中創(chuàng)建散點(diǎn)圖表。文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11Java調(diào)用DOS實(shí)現(xiàn)定時(shí)關(guān)機(jī)的實(shí)例
Java調(diào)用DOS實(shí)現(xiàn)定時(shí)關(guān)機(jī)的實(shí)例,需要的朋友可以參考一下2013-04-04