Netty+SpringBoot+FastDFS+Html5實現聊天App(六)
Netty+SpringBoot+FastDFS+Html5實現聊天App,專案介紹。
Netty+SpringBoot+FastDFS+Html5實現聊天App,專案github連結 。
本章將給聊天App_PigChat加上心跳機制。
為什麼要實現心跳機制
如果沒有特意的設定某些選項或者實現應用層心跳包,TCP空閒 的時候是不會發送任何資料包 。也就是說,當一個TCP的socket,客戶端與服務端誰也不傳送資料 ,會一直保持著連線 。這其中如果有一方異常掉線 (例如宕機、路由被破壞、防火牆切斷連線等),另一端如果沒有傳送資料,永遠也不可能知道 。這對於一些服務型的程式來說,是災難性的後果,將會導致服務端socket資源耗盡 。
舉個簡單的例子,當我們因為特殊情況開啟飛航模式 ,在處理完事件之後再關閉飛航模式,這時候如果再進入應用程式中,我們將以新的channel進入,但是之前的channel還是會保留。
因此,為了保證連線的有效性、及時有效地檢測到一方的非正常斷開,保證連線的資源被有效的利用,我們就會需要一種保活的機制 ,通常改機制兩種處理方式:
1、利用TCP協議層實現的Keepalive;
2、自己在應用層實現心跳包。
實現心跳機制
新建一個HeartBeatHandler用於檢測channel的心跳。
繼承ChannelInboundHandlerAdapter,並重寫其userEventTriggered方法。當客戶端的所有ChannelHandler中4s內沒有write事件,則會觸發userEventTriggered方法。
首先我們判斷evt是否是IdleStateEvent的例項,IdleStateEvent用於觸發使用者事件,包含讀空閒/寫空閒/讀寫空閒。
對evt進行強制履行轉換後,通過state判斷其狀態,只有當其該channel處於讀寫空閒的時候才將這個channel關閉。
/** * @Description: 用於檢測channel的心跳handler *繼承ChannelInboundHandlerAdapter,從而不需要實現channelRead0方法 */ public class HeartBeatHandler extends ChannelInboundHandlerAdapter { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { // 判斷evt是否是IdleStateEvent(用於觸發使用者事件,包含 讀空閒/寫空閒/讀寫空閒 ) if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent)evt;// 強制型別轉換 if (event.state() == IdleState.READER_IDLE) { System.out.println("進入讀空閒..."); } else if (event.state() == IdleState.WRITER_IDLE) { System.out.println("進入寫空閒..."); } else if (event.state() == IdleState.ALL_IDLE) { System.out.println("channel關閉前,users的數量為:" + ChatHandler.users.size()); Channel channel = ctx.channel(); // 關閉無用的channel,以防資源浪費 channel.close(); System.out.println("channel關閉後,users的數量為:" + ChatHandler.users.size()); } } } }
增加心跳支援
在原來的WSServerInitialzer中增加心跳機制的支援。
// ====================== 增加心跳支援 start====================== // 針對客戶端,如果在1分鐘時沒有向服務端傳送讀寫心跳(ALL),則主動斷開 // 如果是讀空閒或者寫空閒,不處理 pipeline.addLast(new IdleStateHandler(8, 10, 12)); // 自定義的空閒狀態檢測 pipeline.addLast(new HeartBeatHandler()); // ====================== 增加心跳支援 end======================