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 ======================