Netty檢查連接斷開的幾種方法


最近項目中需要判定客戶端是否還在線,需要用到心跳檢測機制。這里做個筆記總結一下。

心跳檢測機制:

網絡中接收和發送數據都是通過操作系統的socket實現的。但是如果套接字已經斷開,那發送和接收數據就會出問題。
但如何判斷套接字是否斷開了呢?這就需要建立一種機制,能夠檢測通信對方是否還存活。如果已經斷開,就要釋放資源。這種機制通常采用心跳檢測實現。
所謂的“心跳”就是定時發送一個自定義的結構體(心跳包或心跳幀),讓對方知道自己“在線”,以確保鏈接的有效性。心跳檢測規定定時發送心跳檢測數據包,接收方接心跳包后回復,否則認為連接斷開。

一、Netty心跳檢測方式

1、pipeline加入IdleStateHandler

Netty提供了心跳檢測類IdleStateHandler,它有三個參數,分別是讀超時時間、寫超時時間、讀寫超時時間。
1)readerIdleTime:讀超時時間;
2)writerIdleTime:寫超時時間;
3)allIdleTime:所有類型超時時間;
這里最重要是的readerIdleTime,當設置了readerIdleTime以后,服務端server會每隔readerIdleTime時間去檢查一次channelRead方法被調用的情況,如果在readerIdleTime時間內該channel上的channelRead()方法沒有被觸發,就會調用userEventTriggered方法。

//讀超時時間設置為10s,0表示不監控
ch.pipeline().addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS));
//加入處理事件
ch.pipeline().addLast(new ServerHeartBeat());

2、重寫userEventTriggered方法

重寫ChannelInboundHandlerAdapter處理類的userEventTriggered方法,在方法中處理idleEvent.state() == IdleState.READER_IDLE情況。

public class ServerHeartBeat extends ChannelInboundHandlerAdapter {

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {//超時事件
            IdleStateEvent idleEvent = (IdleStateEvent) evt;
            if (idleEvent.state() == IdleState.READER_IDLE) {//
                ctx.channel().close();
            } else if (idleEvent.state() == IdleState.WRITER_IDLE) {//

            } else if (idleEvent.state() == IdleState.ALL_IDLE) {//全部

            }
        }
        super.userEventTriggered(ctx, evt);
    }
}

二、TCP連接心跳檢測方式

1、TCP心跳機制

上面采用的是Netty的心跳檢測機制,屬於應用層自定義的心跳檢測機制。

應用層實現心跳機制好處:
一是靈活, 應用層心跳可以自由定義,可實現各時間間隔(秒級、毫秒級)的檢測,包里還可以攜帶額外的信息。
二是通用, 應用層的心跳不依賴於底層協議。如果后期想把TCP改為UDP,協議層不提供心跳機制了,但應用層的心跳依舊是通用的。

應用層實現心跳機制壞處:
一是增加開發量, 由於使用特定的網絡框架, 還可能很增加代碼結構的復雜度。
二是額外增加了網絡通信數據包,流量消耗更大。

TCP協議本身也實現了心跳檢測機制。我們也可以使用TCP的心跳機制檢測連接是否斷開。
如果設置了心跳,TCP會在一定時間(比如設置的是3秒鍾)內發送設置的次數的心跳(比如說2次),並且該心跳信息不會影響自己定義的協議。

2、TCP心跳設置

TCP協議自帶的保活功能,使用起來很簡單。Linux環境下,修改/etc/sysctl.conf文件
添加以下內容:

#表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是7200秒(2小時),改為5秒鍾。
net.ipv4.tcp_keepalive_time = 5

#如果對方不予應答,探測包的發送次數
net.ipv4.tcp_keepalive_probes = 5

#探測消息發送的頻率
net.ipv4.tcp_keepalive_intvl = 1

這個是修改的linux tcp發送探測包配置的。修改完成后輸入以下命令生效

/sbin/sysctl -p
/sbin/sysctl -w net.ipv4.route.flush=1

還可以使用echo的方式修改,命令如下:

#echo 5 > /proc/sys/net/ipv4/tcp_keepalive_time
#echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes
#echo 1 > /proc/sys/net/ipv4/tcp_keepalive_intvl 

修改后查看下參數,驗證設置是否已經生效。

#cat /proc/sys/net/ipv4/tcp_keepalive_time  
#cat /proc/sys/net/ipv4/tcp_keepalive_probes  
#cat /proc/sys/net/ipv4/tcp_keepalive_intvl 

這樣設置完成以后,每次客戶端斷開連接后5秒后會執行以下方法。

3、使用Netty,重寫handlerRemoved方法

public class TcpServerHandler extends SimpleChannelInboundHandler<ByteBuf>{

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throw Exception{

        //執行客戶端斷開連接后的業務操作
    }
}

這里的時間設置不一定適合各業務場景,需要根據具體的業務設置合適的時間間隔。有時候網絡不穩定,一個探測包沒有探測到,系統就自動斷開連接了。

三、使用redis保存連接

服務端收到客戶端發送的心跳數據包后,使用redis保存,同時設置超時時間,一旦超時,觸發超時事件,表示客戶端連接出問題了。

比如:心跳時間為60s,那么服務器端收到客戶端心跳數據包后,保存到redis,並設置超時事件為70s(根據實際情況,需要大於心跳時間),一旦超時觸發超時事件,進行連接斷開邏輯處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM