netty(十八)Netty提升 - 連接假死如何處理? 一、連接假死出現原因 二、如何解決連接假死?

一、連接假死出現原因

在網絡編程的領域當中,很多問題都會莫名其妙的出現,讓人措手不及。

其中一種,就是連接假死,那麼連接假死是如何出現的呢?可能存在以下幾種情況:

1)網絡設備出現故障。例如網卡,機房等,底層的 TCP 連接已經斷開了,但應用程序沒有感知到,仍然佔用着資源。

2)公網網絡不穩定,出現丟包。如果連續出現丟包,這時現象就是客戶端數據發不出去,服務端也一直收不到數據,進程一直佔據資源,耗在這兒。

3)應用程序線程阻塞,無法進行數據讀寫。

連接假死會產生以下問題:

1)假死的連接佔用的資源不能自動釋放。

2)向假死的連接發送數據,得到的反饋是發送超時。

二、如何解決連接假死?

Netty提供了一個叫做IdleStateHandler的處理器,用來進行空閒檢測。

其實現方式是:

當Channel有一段時間沒有執行讀、寫或兩者操作時觸發IdleStateEvent。

有三個主要參數:

  • readerIdleTime:一個IdleStateEvent,其狀態是IdleState.READER_IDLE時的指定時間段內,沒有執行讀操作將被觸發。 指定0以禁用。
  • writerIdleTime:一個IdleStateEvent,其狀態是IdleState.WRITER_IDLE時的指定時間段內,沒有執行寫操作將被觸發。 指定0以禁用。
  • allIdleTime:一個IdleStateEvent,其狀態是IdleState.ALL_IDLE時的指定時間段內,沒有進行讀取和寫入都將被觸發。 指定0以禁用

我們正常使用時,也使用這個三個參數的構造即可:

    public IdleStateHandler(
            int readerIdleTimeSeconds,
            int writerIdleTimeSeconds,
            int allIdleTimeSeconds) {

        this(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds,
             TimeUnit.SECONDS);
    }

如上所示,其默認單位是秒,當然也有可以指定單位的構造,還能附帶考慮buf處理數據時間的構造,這裏就不多介紹了。

接下來我們看看如何使用,我們需要將這個處理器添加到pipeline當中,需要注意的是,我們需要自己去處理事件,IdleStateEvent。

關於這個事件的處理,我們不能使用常規的入站或者出站處理器了,此處需要學習一個新的處理器,專門用來處理特殊事件的,我通過下面的代碼進行演示:

服務端接收數據,添加如下代碼:

                    //空閒檢測處理器,檢測5秒內未讀取
                    ch.pipeline().addLast(new IdleStateHandler(5, 0, 0));
                    //可以同時作爲入站和出站處理器,此處用來處理特殊事件
                    ch.pipeline().addLast(new ChannelDuplexHandler() {
                        //用戶事件觸發
                        @Override
                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                            IdleStateEvent stateEvent = (IdleStateEvent) evt;
                            //我們的服務器端只進行數據接收
                            if (stateEvent.state() ==  IdleState.READER_IDLE) {
                                System.out.println("已經5秒沒有接收到數據");
                            }
                        }
                    });

客戶端發送數據,添加如下代碼:

                   //空閒檢測處理器,檢測5秒未寫入
                    ch.pipeline().addLast(new IdleStateHandler(0, 5, 0));
                    //可以同時作爲入站和出站處理器,此處用來處理特殊事件
                    ch.pipeline().addLast(new ChannelDuplexHandler() {
                        //用戶事件觸發
                        @Override
                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                            IdleStateEvent stateEvent = (IdleStateEvent) evt;
                            //我們的服務器端只進行數據接收
                            if (stateEvent.state() ==  IdleState.WRITER_IDLE) {
                                System.out.println("已經5秒沒有寫入數據");
                            }
                        }
                    });

當客戶端和服務端都啓動並建立連接後,如果沒有消息就會打印如下內容:

已經5秒沒有寫入數據
已經5秒沒有寫入數據
已經5秒沒有寫入數據
已經5秒沒有寫入數據
... ...
已經5秒沒有讀取到數據
已經5秒沒有讀取到數據
已經5秒沒有讀取到數據
已經5秒沒有讀取到數據
... ...

其實還有後續的操作,就是,當我們檢測到在一定時間內沒有發生數據寫入或讀取,應該怎麼做?

可以制定一些策略,如連續多少次檢測到空閒等。最終我們要做的其實就是釋放資源:

ctx.channel().close();

上述的代碼實際並不友好,其實比較好的處理方式,需要結合業務區考慮,沒有消息讀取並不代表連接存在問題,可以使用下面的方式,心跳檢測

客戶端:當檢測到沒有數據寫入時,可以定時向服務端發送心跳,比如每5秒就向服務端寫入一次數據。這個時間要小於服務器設置的檢測時間。

服務端:設置一個定時讀取的檢測時間,比如8秒,這樣在8秒內,一定會受到客戶端的心跳,如果超過8秒沒有心跳,則可以認爲連接出現問題,可以關閉channel。


關於空閒檢測的內容,就介紹這麼多,有用的話點個贊再走吧!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章