Netty集成心跳檢測(cè)及其源碼

netty集成心跳檢測(cè)
  1. 需要在pipline里面添加netty自帶的心跳檢測(cè)
// 三個(gè)參數(shù)墅茉,第一個(gè)是當(dāng)多少s沒有讀的時(shí)候執(zhí)行讀空閑,
// 第二個(gè)是當(dāng)多少秒沒有寫的時(shí)候執(zhí)行寫空閑
// 第三個(gè)是當(dāng)多少秒都沒用讀寫的時(shí)候執(zhí)行讀寫空閑
pipeline.addLast(new IdleStateHandler(1,3,5));
  1. 實(shí)現(xiàn)一個(gè)方法來處理當(dāng)發(fā)生上述幾種空閑的情況并添加到pipline里
public class HeartBeatHandler extends ChannelInboundHandlerAdapter {

    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;//強(qiáng)制類型轉(zhuǎn)化
            if (event.state() == IdleState.READER_IDLE) {
//當(dāng)發(fā)生讀空閑時(shí)會(huì)執(zhí)行這個(gè)
//                System.out.println("進(jìn)入讀空閑......");
            } else if (event.state() == IdleState.WRITER_IDLE) {
//當(dāng)發(fā)生寫空閑時(shí)會(huì)執(zhí)行這個(gè)
//                System.out.println("進(jìn)入寫空閑......");
            } else if (event.state() == IdleState.ALL_IDLE) {
//當(dāng)發(fā)生讀寫空閑時(shí)會(huì)執(zhí)行這個(gè)
                Channel channel = ctx.channel();
                //資源釋放
                channel.close();
            }
        }
    }
}

添加HeartBeatHandler到server的pipline里面
pipeline.addLast(new HeartBeatHandler());
源碼分析

netty為什么添加了一個(gè)IdleStateHandler的攔截器之后就可以檢測(cè)了呢 我們先從IdleStateHandler看起

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

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

這邊主要就是初始化我們傳進(jìn)來的數(shù)賦值給readerIdleTime等
    public IdleStateHandler(boolean observeOutput,
            long readerIdleTime, long writerIdleTime, long allIdleTime,
            TimeUnit unit) {
        this.observeOutput = observeOutput;
        if (readerIdleTime <= 0) {
            readerIdleTimeNanos = 0;
        } else {
            readerIdleTimeNanos = Math.max(unit.toNanos(readerIdleTime), MIN_TIMEOUT_NANOS);
        }
        if (writerIdleTime <= 0) {
            writerIdleTimeNanos = 0;
        } else {
            writerIdleTimeNanos = Math.max(unit.toNanos(writerIdleTime), MIN_TIMEOUT_NANOS);
        }
        if (allIdleTime <= 0) {
            allIdleTimeNanos = 0;
        } else {
            allIdleTimeNanos = Math.max(unit.toNanos(allIdleTime), MIN_TIMEOUT_NANOS);
        }
    }
當(dāng)初始化好之后骂铁,此時(shí)我們已經(jīng)把該handler添加到服務(wù)端的pipline中枚驻,
當(dāng)服務(wù)端初始化的時(shí)候會(huì)執(zhí)行該handler的channelRegistered目尖,channelActive等方法
這邊都會(huì)調(diào)用到IdleStateHandler的initialize方法撩独,我們看一下這個(gè)方法

    private void initialize(ChannelHandlerContext ctx) {
//這個(gè)state主要是用來判斷是否初始化
        switch (state) {
        case 1:
        case 2:
            return;
        }

        state = 1;
        initOutputChanged(ctx);
//這邊這個(gè)是干啥的
        lastReadTime = lastWriteTime = ticksInNanos();
//初始化三個(gè)定時(shí)任務(wù)來檢測(cè)校读,這邊是關(guān)鍵弥锄,我們下面來看看這幾個(gè)定時(shí)任務(wù)做了啥事
        if (readerIdleTimeNanos > 0) {
            readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
                    readerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (writerIdleTimeNanos > 0) {
            writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),
                    writerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (allIdleTimeNanos > 0) {
            allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
                    allIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
    }
  • 三個(gè)的實(shí)現(xiàn)方法基本一致丧靡,我們以ReaderIdleTimeoutTask為例
    private final class ReaderIdleTimeoutTask extends AbstractIdleTask {

        ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
            //主要就是this.ctx = ctx;賦值一下
            super(ctx);
        }

        @Override
        protected void run(ChannelHandlerContext ctx) {
            //我們配置的那個(gè)超時(shí)時(shí)間
            long nextDelay = readerIdleTimeNanos;
            if (!reading) {
            //計(jì)算最后一次的讀時(shí)間到現(xiàn)在是否超過了我們?cè)O(shè)置的值
            // nextDelay = nextDelay  - (ticksInNanos() - lastReadTime);
                nextDelay -= ticksInNanos() - lastReadTime;
            }
            //空閑時(shí)間已經(jīng)超過我們?cè)O(shè)置的值
            if (nextDelay <= 0) {
                //重新執(zhí)行該定時(shí)任務(wù)
                readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);
                //判斷是否為第一次發(fā)生空閑事件
                boolean first = firstReaderIdleEvent;
                firstReaderIdleEvent = false;
                try {
                    //新建一個(gè)IdleStateEvent事件
                    IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
                    //這個(gè)方法是觸發(fā)我們上面寫的處理心跳的方法userEventTriggered
                    channelIdle(ctx, event);
                } catch (Throwable t) {
                    ctx.fireExceptionCaught(t);
                }
            } else {
                //如果沒有空閑,則重新執(zhí)行該定時(shí)任務(wù)
                readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }
    }

包裝成一個(gè)IdleStateEvent事件
    protected IdleStateEvent newIdleStateEvent(IdleState state, boolean first) {
        switch (state) {
            case ALL_IDLE:
                return first ? IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT : IdleStateEvent.ALL_IDLE_STATE_EVENT;
            case READER_IDLE:
                return first ? IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT : IdleStateEvent.READER_IDLE_STATE_EVENT;
            case WRITER_IDLE:
                return first ? IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT : IdleStateEvent.WRITER_IDLE_STATE_EVENT;
            default:
                throw new IllegalArgumentException("Unhandled: state=" + state + ", first=" + first);
        }
    }

主要是這個(gè)方法調(diào)用
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
        ctx.fireUserEventTriggered(evt);
    }

用MASK_USER_EVENT_TRIGGERED來查找哪個(gè)是實(shí)現(xiàn)類
    @Override
    public ChannelHandlerContext fireUserEventTriggered(final Object event) {
        invokeUserEventTriggered(findContextInbound(MASK_USER_EVENT_TRIGGERED), event);
        return this;
    }

用MASK_USER_EVENT_TRIGGERED來查找哪個(gè)是實(shí)現(xiàn)類叉讥,handler初始化時(shí)有根據(jù)是否有該方法有執(zhí)行這個(gè)
    private AbstractChannelHandlerContext findContextInbound(int mask) {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while ((ctx.executionMask & mask) == 0);
        return ctx;
    }


    static void invokeUserEventTriggered(final AbstractChannelHandlerContext next, final Object event) {
        ObjectUtil.checkNotNull(event, "event");
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeUserEventTriggered(event);
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeUserEventTriggered(event);
                }
            });
        }
    }

主要是在這邊窘行,調(diào)用真正的實(shí)現(xiàn)方法
    private void invokeUserEventTriggered(Object event) {
        if (invokeHandler()) {
            try {
                ((ChannelInboundHandler) handler()).userEventTriggered(this, event);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireUserEventTriggered(event);
        }
    }
  • 其他寫空閑與讀寫空閑與讀空閑幾乎都是一樣,只是事件不一樣
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末图仓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子但绕,更是在濱河造成了極大的恐慌救崔,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捏顺,死亡現(xiàn)場離奇詭異六孵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)幅骄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門劫窒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拆座,你說我怎么就攤上這事主巍」谙ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵孕索,是天一觀的道長逛艰。 經(jīng)常有香客問我,道長搞旭,這世上最難降的妖魔是什么散怖? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮肄渗,結(jié)果婚禮上镇眷,老公的妹妹穿的比我還像新娘。我一直安慰自己翎嫡,他們只是感情好欠动,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钝的,像睡著了一般翁垂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上硝桩,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天沿猜,我揣著相機(jī)與錄音,去河邊找鬼碗脊。 笑死啼肩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衙伶。 我是一名探鬼主播祈坠,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼矢劲!你這毒婦竟也來了赦拘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤芬沉,失蹤者是張志新(化名)和其女友劉穎躺同,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丸逸,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹋艺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了黄刚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捎谨。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涛救,到底是詐尸還是另有隱情畏邢,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布州叠,位于F島的核電站棵红,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咧栗。R本人自食惡果不足惜逆甜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望致板。 院中可真熱鬧交煞,春花似錦、人聲如沸斟或。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萝挤。三九已至御毅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間怜珍,已是汗流浹背端蛆。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酥泛,地道東北人今豆。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像柔袁,于是被迫代替她去往敵國和親呆躲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

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