亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Netty 心跳檢測

1. 前言

本節,我們主要講解心跳機制 heartbeat,Netty 給我們提供了三個 Handler,分別是 IdleStateHandlerReadTimeoutHandler、WriteTimeoutHandler,主要目的是檢查對方是否有效,也就是說對方是否還在線。

2. 為什么需要心跳機制

了解 TCP: TCP 協議適用于客戶端數量相對比較少,并且通信頻繁的業務場景;Http 協議則適用于客戶端數量比較大的業務場景。因為 Http 是短連接,請求完成即會釋放連接資源,不再占用服務器資源,但是,TCP 則不會,連接成功,則可以多次請求,不會釋放,除非特殊原因導致連接斷開。

面臨問題: 既然長連接是不會釋放連接資源,那么如果很多客戶端只是完成了連接,但是并沒有實際的業務請求操作,那么服務器的資源還是被占用,導致服務器性能下降。

解決辦法: 把那些長期占用連接資源,但是并沒有實際業務操作的連接斷開掉,等它們需要做業務操作的時候,再重連服務器。這樣可以達到即使釋放沒用的資源,提高服務器的性能。

總結,心跳機制主要有以下兩個方面的作用

  1. 定時剔除哪些沒用的連接,減輕服務端的壓力;
  2. 適用于中間件(比如:RPC 框架),服務端規定時間內沒用收到客戶端的心跳數據,則可以認為其宕機,服務端剔除對于的映射關系。

圖片描述

3. 三個核心類講解

為了實現以上需求,Netty 給我們提供了幾個特殊的 Handler 類。

名稱 作用
IdleStateHandler 當連接空閑時間(讀或寫)太長時,將觸發 IdleStateEvent 事件,可以通過 ChannelInboundHandler 中重寫 userEventTrigged 方法來處理該事件。
ReadTimeoutHandler 如果在指定的時間之內沒有發生讀事件,就會拋出這個異常,并且自動關閉連接??梢栽?exectionCaught 方法中處理這個異常。
WriteTimeoutHandler 如果在指定的時間之內沒有發生寫事件,拋出次異常,并且關閉連接??梢栽?exectionCaught 方法中處理這個異常。

IdleStateHandler 構造函數說明

/*
* readerIdleTimeSeconds 讀事件空閑時間,如果為0則表示禁用
* writerIdleTimeSeconds 寫事件空閑時間,如果為0則表示禁用
* allIdleTimeSeconds 讀寫事件空閑時間
*/
public IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) {
    
}

public IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) {
    
}

總結:都是利用定時器調度任務完成。 IdleStateHandler 超時調用 handler 的 userEventTriggered 方法。ReadTimeOutHandler 超時拋出異常,調用 handler 的 exceptionCaught 方法,并且會關閉 channel。

4. 案例測試

4.1 服務端

實例:

ChannelPipeline pipeline = ch.pipeline();

//5秒鐘沒有讀事件,則斷開連接
pipeline.addLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS));
//5秒鐘沒有寫事件,則斷開連接
pipeline.addLast(new WriteTimeoutHandler(5, TimeUnit.SECONDS));
//解碼器
pipeline.addLast(new StringDecoder());
//編碼器
pipeline.addLast(new StringEncoder());
//業務Handler
pipeline.addLast(new HeartBeanHandler());
public class HeartBeanHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("channelRead>>>"+msg+">>>"+ LocalDateTime.now());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught>>>"+cause.getMessage());
    }
}

4.2 客戶端 1

實例:延遲 1 秒鐘,每個 15 秒鐘往服務端發送一次 hello world。

channelFuture.channel().eventLoop().scheduleWithFixedDelay(new Runnable() {
    public void run() {
        channelFuture.channel().writeAndFlush("hello world");
    }    
},1,15, TimeUnit.SECONDS);

服務端執行結果:

channelRead>>>hello world>>>2020-07-26T15:16:08.893
exceptionCaught>>>null

客戶端執行結果:

客戶端關閉了
Process finished with exit code 0

代碼說明:

  1. 客戶端每隔 15 秒發送一次數據;
  2. 服務端如果 5 秒之內沒有讀寫事件,則自動斷開連接;
  3. 從時間設置上來看,客戶端每次發送數據都是超時了,因此,連接會被斷開。

4.3 客戶端 2

實例:延遲 1 秒鐘,每個 3 秒鐘往服務端發送一次 hello world。

channelFuture.channel().eventLoop().scheduleWithFixedDelay(new Runnable() {
    public void run() {
        channelFuture.channel().writeAndFlush("hello world");
    }    
},1,3, TimeUnit.SECONDS);

服務端執行結果:

channelRead>>>hello world>>>2020-07-26T15:15:10.889
channelRead>>>hello world>>>2020-07-26T15:15:13.892
channelRead>>>hello world>>>2020-07-26T15:15:16.893
channelRead>>>hello world>>>2020-07-26T15:15:19.894

代碼說明:

  1. 客戶端每隔 3 秒發送一次數據;
  2. 服務端如果 5 秒之內沒有讀寫事件,則自動斷開;
  3. 從時間設置上來看,客戶端每次發送數據的時間都在超時時間范圍之內,因此,連接不會被斷開。

4.4 特殊說明

ReadTimeoutHandler 和 WriteTimeoutHandler 既可以用于客戶端,也可以用于服務端,或者兩邊同時使用。常見的業務場景如下所示:

場景一: 如果是服務端往客戶端推送消息(消息推送),則 WriteTimeoutHandler 用于服務端,ReadTimeoutHandler 用于客戶端;
場景二: 如果是客戶端主動發起請求的業務(比如:IM),則 WriteTimeoutHandler 用于客戶端,ReadTimeoutHandler 用于服務端。

4.5 IdleStateHandler 的使用

實例:管道中添加 IdleStateHandler

// 空閑檢測
ch.pipeline().addLast(new IdleStateHandler(60,45,20,TimeUnit.SECONDS));
// 業務Handler
ch.pipeline().addLast(new HeartBeatHandler());

5. 小結

本節主要掌握的知識點

  1. 為什么需要做心跳檢測,以及它的常見作用,分別是:主動剔除無用連接減輕服務端壓力、用于中間件的心跳檢測;
  2. Netty 提了三個核心類,分別是 IdleStateHandler、ReadTimeoutHandler、WriteTimeoutHandler,它們的作用以及使用方法。