
1,?EventLoopGroup,EventLoop,EventExecutorGroup,EventExecutor都實(shí)現(xiàn)了ScheduledExecutorService,粗略的說(shuō)他們都是可調(diào)度線程池
2,顧名思義,EventLoopGroup包含一組EventLoop,EventExecutorGroup包含一組EventExecutor。
3,EventExecutorGroup主要覆蓋了父接口里的幾個(gè)方法,返回特定的ScheduledFuture類型。最關(guān)鍵的方法是next,返回一個(gè)可用的EventExecutor,EventLoopGroup提供了上個(gè)register方法用于注冊(cè)channel到EventLoopGroup。
4,EventExecutor中最關(guān)鍵的是inEventLoop方法,用于判斷一個(gè)線程是否是EventExecutor內(nèi)部那個(gè)線程,EventExecutor和EventLoop都是單線程實(shí)現(xiàn)。inEventLoop的主要使用場(chǎng)景是,當(dāng)IO變化時(shí),通過(guò)channel關(guān)聯(lián)的pipeline會(huì)觸發(fā)對(duì)應(yīng)的事件,這些事件對(duì)應(yīng)的執(zhí)行pipeline中的處理鏈中handler的回調(diào)方法,每個(gè)handler添加到pipeline都可以指定自己的EventLoop,如果沒(méi)指定,默認(rèn)使用要添加的pipeline關(guān)聯(lián)的channel注冊(cè)到的EventLoopGroup中的某個(gè)EventLoop。所以channel通過(guò)pipeline調(diào)用handler時(shí),如果handler沒(méi)有單獨(dú)指定EventLoop,那inEventLoop就會(huì)返回true,他倆由同一個(gè)線程處理,直接調(diào)用handler。如果handler單獨(dú)指定了EventLoop,inEventLoop就會(huì)返回false,channel調(diào)用handler時(shí)就把要調(diào)用的方法封裝到Runnable里,然后添加到handler指定的EventLoop的任務(wù)隊(duì)列里,稍后會(huì)由對(duì)應(yīng)的EventLoop中的線程執(zhí)行。
5,EventExecutorGroup有兩個(gè)主要的實(shí)現(xiàn)類:
MultithreadEventExecutorGroup:看名字就知道有多個(gè)線程,哈哈,主要用于注冊(cè)支持NIO的channel。在構(gòu)造時(shí)根據(jù)指定的線程數(shù)量調(diào)用抽象方法newChild初始化EventExecutor數(shù)組(典型的模版方法設(shè)計(jì)模式),每個(gè)EventExecutorGroup類型都有對(duì)應(yīng)的EventExecutor類型,每個(gè)EventLoopGroup都有對(duì)應(yīng)的EventLoop類型。例如:最重要的NioEventLoopGroup的newChild實(shí)現(xiàn)方式如下

第3點(diǎn)里說(shuō)EventExecutorGroup添加了一個(gè)非常重要的方法next,用于返回可用的EventExecutor,對(duì)應(yīng)的選擇策略接口是EventExecutorChooser(策略設(shè)計(jì)模式的典型應(yīng)用場(chǎng)景)

如果executors數(shù)組的長(zhǎng)度是2的指數(shù)就使用上面的,否則使用下面的,主要就是均勻的分布?jí)毫Φ蕉鄠€(gè)線程。
ThreadPerChannelEventLoopGroup:看名字就知道每次有client連接時(shí)就啟用一個(gè)新線程處理。構(gòu)造時(shí)設(shè)置最大線程數(shù),內(nèi)部用Set存儲(chǔ)當(dāng)前活躍的線程,用ConcurrentLinkedQueue存儲(chǔ)當(dāng)前idle狀態(tài)的線程,不支持next方法,有一個(gè)類似的nextChild方法,實(shí)現(xiàn)方法簡(jiǎn)單明了。

6, 注冊(cè)channel到EventLoopGroup,使用EventLoopGroup中某個(gè)EventLoop處理channel的IO

一般regist都發(fā)生在主線程,所以eventLoop.inEventLoop()會(huì)返回false,eventLoop從ScheduledExecutorService繼承了execute方法,第一次執(zhí)行execute時(shí),EventLoop內(nèi)部的線程開始啟動(dòng),啟動(dòng)之后循環(huán)調(diào)用抽象方法run執(zhí)行任務(wù)隊(duì)列中的任務(wù)。

更具體的register實(shí)現(xiàn)都在doRegister抽象方法中(模版方法設(shè)計(jì)模式真是很實(shí)用哦),重點(diǎn)是此時(shí)會(huì)pipeline.fireChannelRegistered()觸發(fā)channelRegistered事件,如果是第一次注冊(cè),還會(huì)觸發(fā)channelActive事件,事件實(shí)現(xiàn)細(xì)節(jié)稍后會(huì)有專門的文章分析。
NIO的doRegister是把channel注冊(cè)到NioEventLoop內(nèi)部的selector上,線程啟動(dòng)之后就不停的select注冊(cè)的IO可用的channel。

handler在添加到channel關(guān)聯(lián)的pipeline時(shí),如果不指定EventLoop,默認(rèn)使用channel注冊(cè)到的EventLoop。所以EventLoop有兩個(gè)職責(zé):監(jiān)控IO并觸發(fā)相應(yīng)的事件和執(zhí)行handler的事件方法。ioRatio控制這兩個(gè)職責(zé)消耗的時(shí)間比例。

OIO的doRegister是空的,啥也沒(méi)干。

oio由于是一個(gè)線程處理一個(gè)channel,直接從任務(wù)隊(duì)列里取任務(wù)執(zhí)行就行了。
7,注冊(cè)成功之后會(huì)觸發(fā)channelActive事件,channlActive默認(rèn)會(huì)觸發(fā)channel和關(guān)聯(lián)的pipeline的read事件,由于read事件屬于outbound handler,所以從tail開始傳播(關(guān)于pipeline稍后分析)。最后會(huì)傳播到pipeline的head,head的read方法會(huì)調(diào)用unsafe.beginRead。
在NIO中的效果是添加對(duì)channel read事件的監(jiān)聽,OIO中的效果是開始讀channel里的數(shù)據(jù)
之前說(shuō)過(guò)EventLoop的職責(zé)不只是處理IO,但是OIO的IO操作都是阻塞的,一旦阻塞就無(wú)法處理handler回調(diào)了,所以需要設(shè)置超時(shí)來(lái)模擬非阻塞。


每次讀完一個(gè)請(qǐng)求之后,都會(huì)關(guān)閉輸入(OIO是關(guān)閉InputStream,NIO是取消監(jiān)控channel的read事件),之后如果設(shè)置了autoread(默認(rèn)為true,如果設(shè)置了false,必須自己在handler中調(diào)用channel.read,否則不會(huì)收到請(qǐng)求),會(huì)再次打開(打開之后,如果有請(qǐng)求到來(lái)(同一個(gè)連接如果還沒(méi)收到響應(yīng),在超時(shí)之前是不能再次發(fā)送請(qǐng)求的吧???),但是現(xiàn)在的請(qǐng)求還沒(méi)處理完,就會(huì)在EventLoop的任務(wù)隊(duì)列里排隊(duì))。然后開始處理這個(gè)請(qǐng)求,一般的pipeline流程是:各種ByteToMessageDecode,接著各種MessageToMessageDecode,轉(zhuǎn)為java對(duì)象之后就是正常的業(yè)務(wù)處理了,每次有要響應(yīng)的數(shù)據(jù)時(shí)都可以調(diào)用write*中間經(jīng)過(guò)各種MessageToMessageEncode和MessageToByteEncode把響應(yīng)寫入ChannelOutboundBuffer,等全部處理完之后,再調(diào)用flush,把整個(gè)buffer寫入channel。
NIO flush的過(guò)程是先直接寫入channel,寫入失敗之后開始監(jiān)聽write事件,等write事件觸發(fā)了,寫完所有請(qǐng)求之后,取消監(jiān)控write事件

OIO的doWrite就比較簡(jiǎn)單了,就是死循環(huán)拼命寫。

一次完整的讀寫過(guò)程就結(jié)束啦。