Netty剖析 - 2. 實(shí)現(xiàn)

前言

本篇文章主要分析Netty的系統(tǒng)結(jié)構(gòu)以及其如何實(shí)現(xiàn)其對外宣稱的特色呜师,如果還未了解Netty的基礎(chǔ)知識职祷,最好先閱讀本系列的第一篇文章Netty剖析 - 1. 基礎(chǔ)

Netty總體結(jié)構(gòu)

image.png

這張圖摘自Netty官網(wǎng)褥赊,其展示的是Netty的模塊結(jié)構(gòu)看幼,總體來說劈榨,Netty分為兩大模塊:

  1. 核心模塊
    核心模塊主要提供的是Netty的一些基礎(chǔ)類和底層接口浙炼,主要包含三部分:
    • 用以提升性能吠谢,減少資源占用的Zero-Copy-Capable Rich Byte Buffer土童,即「零拷貝」緩沖區(qū),Netty里的「零拷貝」與操作系統(tǒng)語境下的「零拷貝」不是同一個概念工坊,具體會在后續(xù)章節(jié)做闡述
    • 統(tǒng)一的API献汗,這是Netty對外宣傳的簡單易用API的一部分,什么意思呢栅组?就是Netty為同步和異步IO提供統(tǒng)一的編程接口雀瓢,舉個例子,如果在前期希望使用BIO玉掸,后續(xù)隨著業(yè)務(wù)變動刃麸,希望改用NIO,只需要改動幾個簡單的初始化參數(shù)司浪,而不需要變動主體流程泊业;相反,如果一開始不是基于Netty啊易,而是直接基于BIO書寫處理流程吁伺,后期想改成NIO,其變動是很大的租谈,畢竟是兩個不同的接口模塊
    • 易擴(kuò)展的事件模型篮奄,這里的重點(diǎn)在于易擴(kuò)展,因?yàn)镹IO本身就是基于事件的IO模型割去,而擴(kuò)展性很好理解窟却,如果一個框架無法擴(kuò)展,那么也就意味著無法應(yīng)對業(yè)務(wù)的變化
  2. 服務(wù)模塊
    既然Netty的核心是IO呻逆,那么其服務(wù)模塊基本也就和IO操作分不開了夸赫,主要有:
    • 網(wǎng)絡(luò)接口數(shù)據(jù)處理相關(guān)服務(wù),如報(bào)文的粘包咖城,拆包處理茬腿,數(shù)據(jù)的加密呼奢,解密等
    • 各網(wǎng)絡(luò)層協(xié)議實(shí)現(xiàn)服務(wù),主要包括傳輸層和應(yīng)用層相關(guān)網(wǎng)絡(luò)協(xié)議的實(shí)現(xiàn)
    • 文件處理相關(guān)服務(wù)

Netty處理架構(gòu)

介紹完Netty的模塊結(jié)構(gòu)切平,我們再來看一下它的處理架構(gòu):


image.png

Netty的架構(gòu)也很清晰握础,就三層:

  1. 底層IO復(fù)用層,負(fù)責(zé)實(shí)現(xiàn)多路復(fù)用
  2. 通用數(shù)據(jù)處理層揭绑,主要對傳輸層的數(shù)據(jù)在進(jìn)和出兩個方向進(jìn)行攔截處理弓候,如編/解碼,粘包處理等
  3. 應(yīng)用實(shí)現(xiàn)層他匪,開發(fā)者在使用Netty的時候基本就在這一層上折騰,同時Netty本身已經(jīng)在這一層提供了一些常用的實(shí)現(xiàn)夸研,如HTTP協(xié)議邦蜜,F(xiàn)TP協(xié)議等

一般來說,數(shù)據(jù)從網(wǎng)絡(luò)傳遞給IO復(fù)用層亥至,IO復(fù)用層收到數(shù)據(jù)后會將數(shù)據(jù)傳遞給上層進(jìn)行處理悼沈,這一層會通過一系列的處理Handler以及應(yīng)用服務(wù)對數(shù)據(jù)進(jìn)行處理,然后返回給IO復(fù)用層姐扮,通過它再傳回網(wǎng)絡(luò)

基于Reactor模式的IO復(fù)用

在Netty處理架構(gòu)圖中絮供,可以看到在IO復(fù)用層上標(biāo)注了一個「Reactor」:


image.png

這個「Reactor」代表的就是其IO復(fù)用層具體的實(shí)現(xiàn)模式 -- Reactor模式

image.png

這張圖是從大名鼎鼎的Doug Lea的一份演講稿中截取下來的,通過這張圖示茶敏,就可以大致明白什么是Reactor模式了壤靶。在Reactor模式中,分為主反應(yīng)組(MainReactor)和子反應(yīng)組(subReactor)以及ThreadPool惊搏,主反應(yīng)組(MainReactor)負(fù)責(zé)處理連接贮乳,連接建立完成以后由主線程對應(yīng)的acceptor將后續(xù)的數(shù)據(jù)處理(read/write)分發(fā)給子反應(yīng)組(subReactor)進(jìn)行處理,而Threadpool對應(yīng)的是業(yè)務(wù)處理線程池恬惯;對應(yīng)代碼為:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);

ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
            ...

在這段代碼中bossGroup對應(yīng)的就是主反應(yīng)組(MainReactor)向拆,workerGroup對應(yīng)的是子反應(yīng)組(subReactor),而NioEventLoopGroup其實(shí)就是一個實(shí)現(xiàn)了Java ExecutorService的線程池酪耳,其中的線程數(shù)可定制浓恳,若不設(shè)置線程數(shù)參數(shù),則該參數(shù)值默認(rèn)為2 * CPU核數(shù)量碗暗,在ServerBootstrap的初始化過程中颈将,會為其添加一個實(shí)現(xiàn)了acceptor機(jī)制的Handler

image.png

而通過ServerBootstrapAcceptor,會在Channel建立后觸發(fā)channelRead()方法讹堤,并在channelRead()內(nèi)將此Channel綁定至子反應(yīng)組對應(yīng)的處理線程吆鹤,后續(xù)的數(shù)據(jù)處理就交于它進(jìn)行處理
image.png

在閱讀這部分源碼的時候需要注意一個點(diǎn),按理來說洲守,連接的建立應(yīng)該是ACCEPT事件疑务,怎么會觸發(fā)channelRead()呢 沾凄?其實(shí)netty內(nèi)部將READACCEPT狀態(tài)一并作為read的觸發(fā)條件
image.png

介紹完了Netty關(guān)于IO復(fù)用層的實(shí)現(xiàn)知残,繼續(xù)看其「易擴(kuò)展」和「關(guān)注點(diǎn)分離」的核心:Pipeline

基于責(zé)任鏈模式的Channel-Pipeline

同樣回過頭去再看Netty處理架構(gòu)圖中的中間層 -- Pipeline


image.png

其字面意思為「管道」屡穗,顧名思義,管道的作用就在于傳輸许溅,而對于Netty來說温鸽,它的管道傳輸?shù)漠?dāng)然就是數(shù)據(jù)了保屯,如果閱讀過上一篇的讀者應(yīng)該知道,在上一篇關(guān)于Netty基礎(chǔ)的介紹里涤垫,提到它有一個很重要的特色就在于:基于事件機(jī)制(Pipeline - Handler)達(dá)成關(guān)注點(diǎn)分離(消息編解碼姑尺,協(xié)議編解碼,業(yè)務(wù)處理)蝠猬,而Pipeline就是實(shí)現(xiàn)這一特色的核心所在切蟋,我們下面來看Netty是如何實(shí)現(xiàn)所謂的「易擴(kuò)展」和「關(guān)注點(diǎn)分離」的

首先,Netty的Pipeline從數(shù)據(jù)傳輸?shù)姆较蛏蟻砜捶譃檫M(jìn)和出榆芦,這個和BIO相同柄粹;其次,最重要的在于Netty在Pipeline上通過責(zé)任鏈模式插入一系列的「Handler」匆绣,這一結(jié)構(gòu)是它能實(shí)現(xiàn)「易擴(kuò)展」和「關(guān)注點(diǎn)分離」的關(guān)鍵驻右。想想看,所謂IO崎淳,不就是數(shù)據(jù)的「進(jìn)」和「出」嗎堪夭?而進(jìn)來干啥呢?當(dāng)然就是需要應(yīng)用邏輯對其處理凯力,那處理完了呢茵瘾?還需要送回給請求方以示響應(yīng),而在進(jìn)的過程中需要哪些處理邏輯咐鹤,這些處理邏輯的先后順序如何拗秘,處理完后出去的過程中需要哪些處理邏輯,這些處理邏輯的順序又是如何祈惶,如果這些都可以方便的配置調(diào)整雕旨,是不是就達(dá)到了Netty宣稱的「易擴(kuò)展」和「關(guān)注點(diǎn)分離」(只需關(guān)注業(yè)務(wù)相關(guān)的Handler,網(wǎng)絡(luò)協(xié)議相關(guān)的Handler直接調(diào)用即可捧请,IO復(fù)用更無須關(guān)注)呢凡涩?

在Netty里,這一實(shí)現(xiàn)機(jī)制的核心類叫做ChannelPipeline

image.png

其中Channel負(fù)責(zé)數(shù)據(jù)通信疹蛉,Handler負(fù)責(zé)邏輯處理活箕,而ChannelPipeline就相當(dāng)于一個由Handler串起來的處理鏈條,在Neety源碼里有一個關(guān)于ChannelPipeline的比較形象的圖形化描述:
image.png

看到?jīng)]有可款,其實(shí)很簡單育韩,就是一個針對不同方向數(shù)據(jù)流的責(zé)任鏈克蚂,其中Inbound對應(yīng)的是輸入流,Outbound對應(yīng)的是輸出流(在這里再多提一句筋讨,責(zé)任鏈模式在很多框架里都有使用埃叭,比如Spring MVC里看到的各種Handler,也是基于責(zé)任鏈的封裝)

強(qiáng)大的ByteBuf

既然是對Netty進(jìn)行分析悉罕,就必然繞不過Netty自己封裝的數(shù)據(jù)緩沖區(qū):ByteBuf赤屋,它是Netty對外宣稱的高性能的重要支撐,另外有必要提一下壁袄,在Netty里其核心緩沖區(qū)類叫「ByteBuf」类早,以便與NIO本身的緩沖區(qū)類「ByteBuffer」做區(qū)分,ByteBuf有如下特點(diǎn):

  • 功能豐富的接口嗜逻,Java NIO本身的緩沖區(qū)接口比較簡單
  • 支持零拷貝莺奔,提升性能,減少資源占用
  • 支持動態(tài)擴(kuò)展
  • 緩沖區(qū)初始塊大小動態(tài)控制
  • 讀寫切換不需要手動調(diào)用clear()变泄,flip();使用過Java NIO的小伙伴應(yīng)該知道恼琼,其在進(jìn)行讀寫切換時需要不停的通過clear()和flip()進(jìn)行模式切換妨蛹,很麻煩
  • 池化,提升性能晴竞,減少資源占用

下面將對上面提到的幾個ByteBuf的重要特性進(jìn)行實(shí)現(xiàn)分析蛙卤,首先來看下ByteBuf是如何避免NIO中那繁瑣的讀寫切換的。我們知道噩死,對于Java NIO的Buffer颤难,其有幾個重要的屬性:positionlimit已维,capacity行嗤,其中position代表的是下一個讀或?qū)懙奈恢茫?code>limit是可被讀或?qū)懙淖罡呶唬?code>capacity就是Buffer的容量了垛耳,之所以要在讀和寫切換的時候進(jìn)行手動操作(clear()栅屏,flip()),主要是因?yàn)樵贜IO中堂鲜,positionlimit在讀的時候代表的是下一個需讀的位和可讀的最高位栈雳,但是在寫的時候又代表下一個需寫的位和可寫的最高位(其實(shí)就是capacity),換句話說這兩個變量在不同的操作場景下有不同的含義缔莲,對應(yīng)值也不同哥纫,所以需要在讀寫切換的時候進(jìn)行手動操作

image.png

而Netty的ByteBuf則對這一點(diǎn)做了改進(jìn),其針對讀寫操作分別增加上了readerIndex痴奏,writerIndex蛀骇,使用的時候不需要考慮讀寫轉(zhuǎn)換
image.png

讀的時候就變動readerIndex的值厌秒,而此時可讀的最高位(對應(yīng)NIO中的limit)其實(shí)就是writerIndex,同理寫的時候就變動writerIndex松靡,此時可寫的最高位(對應(yīng)NIO中的limit)就是capacity简僧,說白了就是兩個變量分別管理讀和寫的操作位,互不沖突雕欺,也就不存在讀寫切換的時候手動操作了岛马;其實(shí)看到這里我們可以發(fā)現(xiàn),NIO在接口設(shè)計(jì)的時候確實(shí)沒有考慮周到屠列,畢竟Netty的這種優(yōu)化并不是有多難啦逆!

零拷貝Buf

在分析ByteBuf的「零拷貝」特性之前,先說說什么是「零拷貝」笛洛,所謂「零拷貝」, 通常指的是在 OS 層面上為了避免在用戶態(tài)(User-space) 與 內(nèi)核態(tài)(Kernel-space) 之間進(jìn)行數(shù)據(jù)拷貝而采取的性能優(yōu)化措施夏志;例如 Linux 提供的 mmap 系統(tǒng)調(diào)用,它可以將一段用戶空間內(nèi)存映射到內(nèi)核空間苛让,當(dāng)映射成功后沟蔑,用戶對這段內(nèi)存區(qū)域的修改可以直接反映到內(nèi)核空間;同樣地狱杰,內(nèi)核空間對這段區(qū)域的修改也直接反映用戶空間瘦材。正因?yàn)橛羞@樣的映射關(guān)系,我們就不需要在用戶態(tài)(User-space) 與內(nèi)核態(tài)(Kernel-space) 之間拷貝數(shù)據(jù)仿畸,從而提高了數(shù)據(jù)傳輸?shù)男适匙兀粚τ贘ava的網(wǎng)絡(luò)操作來說,網(wǎng)絡(luò)接口在收到數(shù)據(jù)的時候需要先將數(shù)據(jù)復(fù)制到內(nèi)核內(nèi)存错沽,然后在從內(nèi)核內(nèi)存復(fù)制到用戶內(nèi)存簿晓,同理往網(wǎng)絡(luò)接口發(fā)數(shù)據(jù)也是先將數(shù)據(jù)從用戶內(nèi)存復(fù)制到內(nèi)核內(nèi)存,再從內(nèi)核內(nèi)存中將數(shù)據(jù)傳給網(wǎng)絡(luò)接口千埃,所以如果是直接操縱內(nèi)核內(nèi)存憔儿,無疑處理的性能會更好

回到Netty,Netty中的 「零拷貝」與上面我們所提到到 OS 層面上的 「零拷貝」其實(shí)不太一樣镰禾,Netty的 「零拷貝」 完全是在用戶態(tài)里的皿曲,或者說更多的是偏向于減少JVM內(nèi)的數(shù)據(jù)操作,具體體現(xiàn)在如下幾個方面:

  • 通過 CompositeByteBuf類吴侦,將多個ByteBuf合并為一個邏輯上的ByteBuf屋休,避免了各個ByteBuf之間的拷貝
  • 通過wrap操作,將byte[]數(shù)組备韧、ByteBuf劫樟、ByteBuffer等多個數(shù)據(jù)容器合并成一個ByteBuf對象,進(jìn)而避免了拷貝操作
  • 通過slice操作,將ByteBuf分解為多個共享同一個存儲區(qū)域的ByteBuf叠艳,避免了內(nèi)存的拷貝
  • 通過FileRegion包裝的FileChannel.tranferTo實(shí)現(xiàn)文件傳輸奶陈,將文件緩沖區(qū)的數(shù)據(jù)發(fā)送到目標(biāo)Channel,避免了傳統(tǒng)通過循環(huán)write方式導(dǎo)致的內(nèi)存拷貝問題

這些操作之所以能避免不必要的拷貝操作附较,其實(shí)就在于內(nèi)部對數(shù)據(jù)進(jìn)行的是邏輯操作而非物理操作吃粒,操作完成后根據(jù)各邏輯引用的數(shù)據(jù)信息(大小,位置等)重新計(jì)算ByteBuf內(nèi)部的控制屬性(limit拒课,capacity徐勃,readerIndexwriterIndex)早像,如通過CompositeByteBuf將原本兩個分別表示head和body的buffer組裝成一個buffer:

image.png

雖然看起來CompositeByteBuf是由兩個ByteBuf組合而成的僻肖,不過在CompositeByteBuf內(nèi)部,這兩個ByteBuf都是單獨(dú)存在的(指針引用)卢鹦,CompositeByteBuf只是邏輯上是一個整體臀脏;這樣在數(shù)據(jù)操作的時候不需要對數(shù)據(jù)進(jìn)行物理挪動,只需要操作數(shù)據(jù)引用并計(jì)算關(guān)鍵因子即可冀自,這種方式不但能提升性能揉稚,還可以減少內(nèi)存占用,值得借鑒

Buf池化

在Netty中熬粗,ByteBuf用來作為數(shù)據(jù)的容器窃植,是一種會被頻繁創(chuàng)建和銷毀的對象,ByteBuf需要的內(nèi)存空間荐糜,可以在 JVM Heap 中申請分配,也可以在Direct Memory(堆外內(nèi)存)中申請葛超,其中在 Direct Memory 中分配的ByteBuf暴氏,其創(chuàng)建和銷毀的代價比在 JVM Heap 中的更高,但拋開哪個代價高哪個代價低不說绣张,光是頻繁創(chuàng)建和頻繁銷毀這一點(diǎn)答渔,就已奠定了效率不高的基調(diào)。Netty為了解決這個問題侥涵,引入了池化技術(shù)沼撕,池化技術(shù)的思想不復(fù)雜,和線程池思想類似芜飘,說白了就是對一些可重用的對象用完不回收务豺,后面需要再次使用,以減少創(chuàng)建和銷毀對象帶來的資源損耗嗦明,下面結(jié)合Netty源碼對其池化技術(shù)做剖析

首先看ByteBuf,它實(shí)現(xiàn)了ReferenceCounted接口,表明該類是一個引用計(jì)數(shù)管理對象

public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf>

而引用計(jì)數(shù)就是實(shí)現(xiàn)池化的關(guān)鍵技術(shù)點(diǎn)(不過并非只有池化的 ByteBuf 才有引用計(jì)數(shù)奔浅,非池化的也會有引用)馆纳,繼續(xù)看ReferenceCounted接口,它定義了這幾個方法:

public interface ReferenceCounted {
    int refCnt();

    ReferenceCounted retain();

    ReferenceCounted retain(int increment);

    boolean release();

    boolean release(int decrement);
}

每一個引用計(jì)數(shù)對象汹桦,都維護(hù)了一個自身的引用計(jì)數(shù)鲁驶,當(dāng)?shù)谝淮伪粍?chuàng)建時,引用計(jì)數(shù)為1舞骆,通過refCnt()方法可以得到當(dāng)前的引用計(jì)數(shù)钥弯,retain()retain(int increment)增加自身的引用計(jì)數(shù)值,而release()release(int increment)則減少當(dāng)前的引用計(jì)數(shù)值葛作,如果引用計(jì)數(shù)值為 0寿羞,并且當(dāng)前的 ByteBuf 被釋放成功,那這兩個方法的返回值就為true赂蠢。而具體如何釋放绪穆,各種不同類型的ByteBuf自己決定,如果是池化的ByteBuf虱岂,那么就會重新進(jìn)池子玖院,以待重用;如果是非池化的第岖,則銷毀底層的字節(jié)數(shù)組引用或者釋放對應(yīng)的堆外內(nèi)存难菌。具體的邏輯在AbstractReferenceCountedByteBuf類中可以看到:

    @Override
    public final boolean release() {
        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, -1);
            }

            if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
                if (refCnt == 1) {
                    deallocate();
                    return true;
                }
                return false;
            }
        }
    }

釋放對象的方法定義在 deallocate() 方法里,它是個抽象方法蔑滓,既然是抽象的郊酒,那么就需要子類自行實(shí)現(xiàn),對于非池化的 HeapByteBuf 來說键袱,釋放對象實(shí)際上就是釋放底層字節(jié)數(shù)組的引用:

    @Override
    protected void deallocate() {
        array = null;
    }

對于非池化的DirectByteBuf來說燎窘,釋放對象實(shí)際上就是釋放堆外內(nèi)存:

    @Override
    protected void deallocate() {
        ByteBuffer buffer = this.buffer;
        if (buffer == null) {
            return;
        }

        this.buffer = null;

        if (!doNotFree) {
            PlatformDependent.freeDirectBuffer(buffer);
        }

        if (leak != null) {
            leak.close();
        }
    }

對于池化的 ByteBuf 來說,就是把自己歸還到對象池里:

    @Override
    protected final void deallocate() {
        if (handle >= 0) {
            final long handle = this.handle;
            this.handle = -1;
            memory = null;
            chunk.arena.free(chunk, handle);
            if (leak != null) {
                leak.close();
            } else {
                recycle();
            }
        }
    }

熟悉JVM GC的同學(xué)應(yīng)該對這個引用計(jì)數(shù)的機(jī)制不會感到陌生蹄咖,因?yàn)镴VM在判斷一個Java對象是否存活時有一種方式使用的就是計(jì)數(shù)法褐健;另外Netty的池化緩存在實(shí)現(xiàn)上借鑒了buddy allocation和slab allocation的思想并進(jìn)行了比較復(fù)雜的設(shè)計(jì)(buddy allocation是基于一定規(guī)則對內(nèi)存進(jìn)行分割,回收時進(jìn)行合并澜汤,盡可能保證系統(tǒng)有足夠的連續(xù)內(nèi)存蚜迅;而slab allocation是把內(nèi)存分割為大小不等的內(nèi)存塊,請求內(nèi)存是分配最貼近請求size的內(nèi)存塊俊抵,避免內(nèi)存浪費(fèi))谁不,可以減少對象的創(chuàng)建與銷毀對性能的影響,因?yàn)榫彌_區(qū)對象的創(chuàng)建與銷毀會占用內(nèi)存帶寬以及GC資源徽诲,另外由于池化緩存本身比較復(fù)雜拍谐,如線程私有池與全局共有池烛缔,其聲明與釋放都需要手動處理(比如本地池內(nèi)的緩沖區(qū)對象如果不是在同一個線程內(nèi)釋放就會導(dǎo)致內(nèi)存泄漏,這也是為什么JVM GC的時候需要有Stop The World)轩拨,Netty提供了內(nèi)存泄漏監(jiān)控工具ResourceLeakDetector践瓷,如果發(fā)生了內(nèi)存泄漏,它會通過日志記錄并提醒亡蓉,這個工具主要是防止對象被GC的時候其占用的資源沒有被釋放(如內(nèi)存)晕翠,或者沒有執(zhí)行release方法

也許有人會說,既然池化緩存實(shí)現(xiàn)復(fù)雜砍濒,用起來還得防止內(nèi)存泄漏淋肾,那么它到底能給性能帶來多大提升呢?我們可以看下Twitter對Netty池化緩存做的性能測試結(jié)果:


image.png

這張圖的Y軸顯示的是創(chuàng)建對象花費(fèi)的時間爸邢,而X軸代表的是所創(chuàng)建對象的大小樊卓,同時在實(shí)驗(yàn)中,使用了四種不同的對象杠河,分別是非池化堆內(nèi)存對象(Unpooled Heap)碌尔,池化堆內(nèi)存對象(Pooled Heap),非池化直接內(nèi)存對象(Unpooled Direct)券敌,池化直接內(nèi)存對象(Pooled Direct)唾戚。結(jié)果現(xiàn)實(shí),隨著被創(chuàng)建對象大小的增加待诅,池化技術(shù)的優(yōu)勢愈加明顯叹坦,當(dāng)然當(dāng)對象很小時,池化反而不如JVM本身的對象創(chuàng)建性能(可以結(jié)合ByteBuf的實(shí)現(xiàn)原理卑雁,想想為什么募书?)

除了對象創(chuàng)建的性能,Twitter還測試了使用池化技術(shù)時GC相關(guān)的表現(xiàn)测蹲,實(shí)驗(yàn)?zāi)M了在16000個連接下锐膜,對256byte大小的數(shù)據(jù)包進(jìn)行循環(huán)傳輸:


image.png

結(jié)果表明,相對于非池化弛房,池化的GC停頓減少了近4倍,而垃圾的增長也慢了4倍而柑。所以說文捶,Netty對ByteBuf進(jìn)行的復(fù)雜的重寫還是值得的

NIO epoll死循環(huán)問題及Netty解決方案

最后說說Netty是如何解決著名的「NIO epoll死循環(huán)」問題的。什么是「NIO epoll死循環(huán)」呢媒咳?在Linux系統(tǒng)中粹排,當(dāng)某個socket的連接突然中斷后,會重設(shè)事件集eventSet涩澡,而eventSet的重設(shè)就會導(dǎo)致Selector被喚醒(但其實(shí)這個時候是沒有任何事件需要處理的顽耳,select()方法應(yīng)該還是處于阻塞狀態(tài)),雖然被喚醒了,但其實(shí)是沒有事件需要處理的射富,所以就又返回select()方法之前(正常情況下是處理完事件重新回去被select()阻塞)膝迎,此時select()方法還是會直接返回,如此反復(fù)便造成死循環(huán):

image.png

這個問題的原因本質(zhì)上就是NIO的Selector實(shí)現(xiàn)有問題胰耗,Netty解決的方式其實(shí)比較簡單粗暴限次,它會記錄一段時間內(nèi)空輪詢的次數(shù),如果超過一定閾值柴灯,就認(rèn)為這個bug出現(xiàn)了卖漫,此時會重新生成一個新的selector取代舊的selector,避免死循環(huán)赠群,具體的處理代碼在NioEventLoop中:
image.png

Netty主要類關(guān)系圖

這里貼一張Netty主要實(shí)現(xiàn)類的關(guān)系圖羊始,對需要閱讀Netty源碼的小伙伴可能有一個參考作用


image.png

總結(jié)

本篇主要介紹了Netty的總體架構(gòu),并對Netty的一些重要的實(shí)現(xiàn)機(jī)制進(jìn)行了簡單的剖析查描,如果在閱讀本篇時發(fā)現(xiàn)對一些基礎(chǔ)的概念和知識不是很了解突委,可以閱讀本系列的第一篇Netty剖析 - 1. 基礎(chǔ)進(jìn)行相關(guān)學(xué)習(xí),如果希望對Netty的實(shí)現(xiàn)有更深入的了解叹誉,推薦去Netty的官網(wǎng)鸯两,或者閱讀Netty源碼,如果還希望了解Netty其他相關(guān)知識长豁,也可以閱讀本系列的最后一篇文章Netty剖析 - 3. 總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钧唐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匠襟,更是在濱河造成了極大的恐慌钝侠,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酸舍,死亡現(xiàn)場離奇詭異帅韧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)啃勉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門忽舟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人淮阐,你說我怎么就攤上這事叮阅。” “怎么了泣特?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵浩姥,是天一觀的道長。 經(jīng)常有香客問我状您,道長勒叠,這世上最難降的妖魔是什么兜挨? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮眯分,結(jié)果婚禮上拌汇,老公的妹妹穿的比我還像新娘。我一直安慰自己颗搂,他們只是感情好担猛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丢氢,像睡著了一般傅联。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疚察,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天蒸走,我揣著相機(jī)與錄音,去河邊找鬼貌嫡。 笑死比驻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岛抄。 我是一名探鬼主播别惦,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夫椭!你這毒婦竟也來了掸掸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蹭秋,失蹤者是張志新(化名)和其女友劉穎扰付,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仁讨,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡羽莺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洞豁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盐固。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丈挟,靈堂內(nèi)的尸體忽然破棺而出刁卜,到底是詐尸還是另有隱情,我是刑警寧澤礁哄,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站溪北,受9級特大地震影響桐绒,放射性物質(zhì)發(fā)生泄漏夺脾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一茉继、第九天 我趴在偏房一處隱蔽的房頂上張望咧叭。 院中可真熱鬧,春花似錦烁竭、人聲如沸菲茬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婉弹。三九已至,卻和暖如春终吼,著一層夾襖步出監(jiān)牢的瞬間镀赌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工际跪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留商佛,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓姆打,卻偏偏與公主長得像良姆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子幔戏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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

  • 1玛追、Netty基礎(chǔ)入門 Netty是由JBOSS提供的一個java開源框架。Netty提供異步的评抚、事件驅(qū)動的網(wǎng)絡(luò)應(yīng)...
    我是嘻哈大哥閱讀 4,690評論 0 31
  • 作者:李林鋒 原文:http://www.infoq.com/cn/articles/netty-high-per...
    楊鑫科閱讀 3,975評論 0 64
  • 前奏 https://tech.meituan.com/2016/11/04/nio.html 綜述 netty通...
    jiangmo閱讀 5,857評論 0 13
  • 頭先出豹缀,開啟了漫長人生; 腳先硬慨代,踏遍了大千世界邢笙; 臉先長,顯示了飽經(jīng)滄桑侍匙; 心先涼氮惯,透出了生活現(xiàn)實(shí)。
    T1M_c687閱讀 209評論 0 0
  • 公司準(zhǔn)備召開檢討會要求對同事提出意見想暗,下面是職員A,B和領(lǐng)導(dǎo)C的對話妇汗。 A:怎么提意見又不得罪人啊说莫? B:這好辦啊...
    hello0905閱讀 256評論 0 2