Netty心跳基本檢測機制

Netty心跳基本檢測機制

首先赡勘,了解下為什么需要心跳从橘?
假設(shè)客戶端(如手機矩距,PAD)與服務(wù)器端已經(jīng)建立了長連接拗盒,客戶端開啟飛行模式或者強制關(guān)機了,服務(wù)器端的handlerRemoved方法不會被調(diào)用锥债,因為感知不到锣咒。此時,客戶端往服務(wù)器端發(fā)送的消息無法到達赞弥。如何處理這種情況呢毅整?那么就是通過心跳來做的,每隔一段時間客戶端向服務(wù)器端發(fā)送心跳包绽左,客戶端經(jīng)過一段時間收到后悼嫉,客戶端認為與服務(wù)器端連接是正常的。如果客戶端經(jīng)過一段時間沒有收到心跳包拼窥,客戶端則認為與服務(wù)端無法通信戏蔑,由客戶端發(fā)起斷開連接,然后重新向服務(wù)器端發(fā)起新的連接鲁纠。

Netty提供了空閑狀態(tài)監(jiān)測的處理器总棵。IdleStateHandler
我們來看下Javadoc說明

/**
 * Triggers an {@link IdleStateEvent} when a {@link Channel} has not performed
 * read, write, or both operation for a while.
 *
 * <h3>Supported idle states</h3>
 * <table border="1">
 * <tr>
 * <th>Property</th><th>Meaning</th>
 * </tr>
 * <tr>
 * <td>{@code readerIdleTime}</td>
 * <td>an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE}
 *     will be triggered when no read was performed for the specified period of
 *     time.  Specify {@code 0} to disable.</td>
 * </tr>
 * <tr>
 * <td>{@code writerIdleTime}</td>
 * <td>an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE}
 *     will be triggered when no write was performed for the specified period of
 *     time.  Specify {@code 0} to disable.</td>
 * </tr>
 * <tr>
 * <td>{@code allIdleTime}</td>
 * <td>an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE}
 *     will be triggered when neither read nor write was performed for the
 *     specified period of time.  Specify {@code 0} to disable.</td>
 * </tr>
 * </table>
 *
 * <pre>
 * // An example that sends a ping message when there is no outbound traffic
 * // for 30 seconds.  The connection is closed when there is no inbound traffic
 * // for 60 seconds.
 *
 * public class MyChannelInitializer extends {@link ChannelInitializer}&lt;{@link Channel}&gt; {
 *     {@code @Override}
 *     public void initChannel({@link Channel} channel) {
 *         channel.pipeline().addLast("idleStateHandler", new {@link IdleStateHandler}(60, 30, 0));
 *         channel.pipeline().addLast("myHandler", new MyHandler());
 *     }
 * }
 *
 * // Handler should handle the {@link IdleStateEvent} triggered by {@link IdleStateHandler}.
 * public class MyHandler extends {@link ChannelDuplexHandler} {
 *     {@code @Override}
 *     public void userEventTriggered({@link ChannelHandlerContext} ctx, {@link Object} evt) throws {@link Exception} {
 *         if (evt instanceof {@link IdleStateEvent}) {
 *             {@link IdleStateEvent} e = ({@link IdleStateEvent}) evt;
 *             if (e.state() == {@link IdleState}.READER_IDLE) {
 *                 ctx.close();
 *             } else if (e.state() == {@link IdleState}.WRITER_IDLE) {
 *                 ctx.writeAndFlush(new PingMessage());
 *             }
 *         }
 *     }
 * }
 *
 * {@link ServerBootstrap} bootstrap = ...;
 * ...
 * bootstrap.childHandler(new MyChannelInitializer());
 * ...
 * </pre>
 *
 * @see ReadTimeoutHandler
 * @see WriteTimeoutHandler
 */
public class IdleStateHandler extends ChannelDuplexHandler {

}

在一定時間內(nèi)當(dāng)有讀、寫或者讀寫沒有被執(zhí)行的時候改含,會觸發(fā)一個空閑狀態(tài)監(jiān)測事件情龄。
從Javadoc中我們也看到了基本的示例代碼,理論+實踐模式 :)
進一步看下構(gòu)造方法:

/**
     * Creates a new instance firing {@link IdleStateEvent}s.
     *
     * @param readerIdleTimeSeconds
     *        an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE}
     *        will be triggered when no read was performed for the specified
     *        period of time.  Specify {@code 0} to disable.
     * @param writerIdleTimeSeconds
     *        an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE}
     *        will be triggered when no write was performed for the specified
     *        period of time.  Specify {@code 0} to disable.
     * @param allIdleTimeSeconds
     *        an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE}
     *        will be triggered when neither read nor write was performed for
     *        the specified period of time.  Specify {@code 0} to disable.
     */
    public IdleStateHandler(
            int readerIdleTimeSeconds,
            int writerIdleTimeSeconds,
            int allIdleTimeSeconds) {

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

構(gòu)造方法中提供了三個參數(shù)捍壤,第一個參數(shù)readerIdleTimeSeconds是指給定時間內(nèi)沒有讀動作骤视,則觸發(fā)READER_IDLE事件;第二個參數(shù)writerIdleTimeSeconds是指給定時間內(nèi)服務(wù)端沒有寫入動作則觸發(fā)WRITER_IDLE事件鹃觉,第三個參數(shù)allIdleTimeSeconds表示給定實踐內(nèi)沒有發(fā)生讀或者寫動作专酗,則觸發(fā)ALL_IDLE事件。

我們可以編寫自定義處理器盗扇,監(jiān)測空閑狀態(tài)祷肯,繼承SimpleChannelInboundHandler沉填,同時,重寫
userEeventTriggered(ChannelHandlerContext ctx佑笋,Object evt)方法拜轨,兩個參數(shù)分別是上下文對象,事件對象允青。userEeventTriggered方法是SimpleChannelInboundHandler父類ChannelInboundHandlerAdapter抽象適配類的方法。
當(dāng)有操作操作超出指定空閑秒數(shù)時卵沉,便會觸發(fā)UserEventTriggered事件颠锉。

IdleState代表了Channel的空心閑狀態(tài)枚舉,三種狀態(tài)史汗,包括READ_IDLE琼掠、WRITE_IDLE、ALL_IDLE

下面附上示例代碼:

  1. Main方法啟動類停撞,這里我們增加了LoggerHandler日志處理器
public class MyIdleServer {

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new MyIdleServerInitializer());

            ChannelFuture channelFuture = bootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
  1. 編寫MyIdleServerInitializer瓷蛙,初始化處理器鏈,增加了IdleStateHandler空閑事件處理器.
public class MyIdleServerInitializer extends ChannelInitializer<SocketChannel>{

    @Override protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline channelPipeline = ch.pipeline();

        channelPipeline.addLast(new IdleStateHandler(6, 4, 10));
        channelPipeline.addLast(new MyIdleServerHandler());
    }
}
  1. 編寫自定義處理器MyIdleServerHandler戈毒,重寫了channelRead0艰猬,目的主要為了測試寫超時觸發(fā)事件.
public class MyIdleServerHandler extends SimpleChannelInboundHandler<Object> {

    @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        int i=0;
        while (i< 3) {
            sleep(3000);
            System.out.println("服務(wù)端執(zhí)行寫入操作3s");
            ctx.channel().writeAndFlush("服務(wù)端寫個客戶端數(shù)據(jù)");
            i++;
        }
    }

    @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvt = (IdleStateEvent) evt;

            String eventType = null; // 空閑事件類型

            switch (idleStateEvt.state()) {
            case READER_IDLE:
                eventType = "讀空閑";
                break;
            case WRITER_IDLE:
                eventType = "寫空閑";
                break;
            case ALL_IDLE:
                eventType = "讀寫空閑";
                break;
            }

            System.out.println("客戶端地址:" + ctx.channel().remoteAddress() + "超時事件被觸發(fā),eventType:" + eventType);
            ctx.channel().close();
        }
    }
}
  1. 啟動MyIdleServer后埋市,使用MyChatClient請參見Netty多客戶端通信一章
    因設(shè)置寫超時時間設(shè)置4秒冠桃,MyChatClient不輸入任何內(nèi)容,結(jié)果如下:
MyChatClient不輸入任何內(nèi)容

當(dāng)MyChatClient輸入內(nèi)容后道宅,執(zhí)行結(jié)果會看到等到channelRead0執(zhí)行完寫入操作后食听,超過寫超時時間才會觸發(fā)WRITE_IDLE狀態(tài).

寫入操作完成后觸發(fā)事件
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市污茵,隨后出現(xiàn)的幾起案子樱报,更是在濱河造成了極大的恐慌,老刑警劉巖泞当,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迹蛤,死亡現(xiàn)場離奇詭異,居然都是意外死亡襟士,警方通過查閱死者的電腦和手機笤受,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敌蜂,“玉大人箩兽,你說我怎么就攤上這事≌潞恚” “怎么了汗贫?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵身坐,是天一觀的道長。 經(jīng)常有香客問我落包,道長部蛇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任咐蝇,我火速辦了婚禮涯鲁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘有序。我一直安慰自己抹腿,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布旭寿。 她就那樣靜靜地躺著警绩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盅称。 梳的紋絲不亂的頭發(fā)上肩祥,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機與錄音缩膝,去河邊找鬼混狠。 笑死,一個胖子當(dāng)著我的面吹牛疾层,可吹牛的內(nèi)容都是我干的檀蹋。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼云芦,長吁一口氣:“原來是場噩夢啊……” “哼俯逾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起舅逸,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤桌肴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后琉历,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坠七,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年旗笔,在試婚紗的時候發(fā)現(xiàn)自己被綠了彪置。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝇恶,死狀恐怖拳魁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撮弧,我是刑警寧澤潘懊,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布姚糊,位于F島的核電站,受9級特大地震影響授舟,放射性物質(zhì)發(fā)生泄漏救恨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一释树、第九天 我趴在偏房一處隱蔽的房頂上張望肠槽。 院中可真熱鬧,春花似錦奢啥、人聲如沸秸仙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炊汤,卻和暖如春正驻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抢腐。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工姑曙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迈倍。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓伤靠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親啼染。 傳聞我的和親對象是個殘疾皇子宴合,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)迹鹅,斷路器卦洽,智...
    卡卡羅2017閱讀 134,639評論 18 139
  • 本文是Netty文集中“Netty 源碼解析”系列的文章。主要對Netty的重要流程以及類進行源碼解析斜棚,以使得我們...
    tomas家的小撥浪鼓閱讀 2,536評論 2 8
  • Channel 與 ChannelPipeline在Netty中每個Channel都有且僅有一個ChannelPi...
    水欣閱讀 2,156評論 0 1
  • 從三月份找實習(xí)到現(xiàn)在阀蒂,面了一些公司,掛了不少弟蚀,但最終還是拿到小米蚤霞、百度、阿里义钉、京東昧绣、新浪、CVTE捶闸、樂視家的研發(fā)崗...
    時芥藍閱讀 42,218評論 11 349
  • 一場試聽課滞乙,一次練習(xí)奏纪,所謂的專業(yè)就是如何將你的總體能力和狀態(tài),完美呈現(xiàn)的過程斩启。 看著臺上的李老師緊張的狀態(tài)序调,語無倫...
    任亞閱讀 593評論 0 0