本文主要對Netty的啟動流程的源碼進(jìn)行解析,首先來一段啟動流程的代碼(紅框就為啟動流程的代碼):本文是Netty文集中“Netty 源碼解析”系列的文章。主要對Netty的重要流程以及類進(jìn)行源碼解析,以使得我們更好的去使用Netty。Netty是一個非常優(yōu)秀的網(wǎng)絡(luò)框架,對其源碼解讀的過程也是不斷學(xué)習(xí)的過程。
重要類介紹
在講解源碼之前,先對該篇文字這涉及到的幾個Netty中的重要的類進(jìn)行簡單的介紹。
EventLoopGroup
事件循環(huán)組(NioEventLoopGroup是異步事件循環(huán)組)。EventLoopGroup中包含了多個EventLoop。主要提供了兩類方法:
① next()方法用于返回下一個EventLoop來使用。
② register方法,來將一個Channel注冊到EventLoop當(dāng)中,同時返回一個ChannelFuture,當(dāng)注冊完成的時候這個ChannelFuture將得到一個通知。可以見該方法是一個異步的方法,在調(diào)用完register后會立即返回,然后我們根據(jù)ChannelFuture中的相關(guān)方法來判斷注冊操作是否完成。
NioEventLoopGroup ——— 異步事件循環(huán)組
它是MultithreadEventLoopGroup的一個實(shí)現(xiàn),它是用于基于Channel的NIO Selector。
EventLoop
事件循環(huán)類。將處理一個已經(jīng)注冊到該EventLoop的Channel的所有I/O操作。
NioEventLoop
NioEventLoop是一個SingleThreadEventLoop的實(shí)現(xiàn),該類注冊Channel到一個Selector中,并使用了IO多路復(fù)用在這個NioEventLoop中。
在NioEventLoop中的整個生命周期只會和一個線程綁定。因此與這個NioEventLoop關(guān)聯(lián)的Channel的IO事件都會在這個線程中完成。
SingleThreadEventExecutor:
一個繼承了OrderedEventExecutor的抽象類,它會執(zhí)行所有提交的任務(wù)在一個單一的
線程中。而OrderedEventExecutor作為一個標(biāo)記接口,它會執(zhí)行所有提交的任務(wù)以有序/連續(xù)的方式。
SingleThreadEventExecutor通過持有一個MpscQueue taskQueue成員變量,來維護(hù)提交上來的任務(wù)。
SingleThreadEventExecutor中持有的executor成員變量是一個ThreadPerTaskExecutor對象,通過ThreadPerTaskExecutor來啟動一個線程作為SingleThreadEventExecutor執(zhí)行器(該執(zhí)行器來自于NioEventLoopGroup)中執(zhí)行任務(wù)的線程,也就是當(dāng)前EventLoop生命周期所關(guān)聯(lián)的線程。
SingleThreadEventExecutor中還維護(hù)有該線程的五個狀態(tài):a)ST_NOT_STARTED;b)ST_STARTED;c)ST_SHUTTING_DOWN;d)ST_SHUTDOWN;e)ST_TERMINATED。
SingleThreadEventExecutor的execute(Runnable task)方法:
execute方法會接收一個個任務(wù),將任務(wù)依次放入taskQueue中。并且如果當(dāng)前執(zhí)行提交任務(wù)的線程不是SingleThreadEventExecutor執(zhí)行任務(wù)的那個唯一線程(即,EventLoop所關(guān)聯(lián)的線程)并且該唯一線程還未被創(chuàng)建啟動,則通過ThreadPerTaskExecutor.execute(Runnable)來創(chuàng)建并啟動執(zhí)行任務(wù)的唯一線程。
也就是說,SingleThreadEventExecutor的任務(wù)線程會在滿足如下條件時被創(chuàng)建并執(zhí)行:
a) 提交任務(wù)的線程不為EventLoop所關(guān)聯(lián)的線程
b) EventLoop所關(guān)聯(lián)的線程還不存在,即EventLoop所關(guān)聯(lián)的線程的狀態(tài)為ST_NOT_STARTED
值得說明的一點(diǎn)是,我們的啟動程序“serverBootstrap.bind(5566)”就滿足??的條件,也就會觸發(fā)EventLoop所關(guān)聯(lián)的線程創(chuàng)建并執(zhí)行。至于具體bind流程,我們下面會進(jìn)行詳細(xì)說明,往下看你就曉得為什么bind(int port)滿是于上面的條件了。
SingleThreadEventExecutor的doStartThread()方法:會調(diào)用SingleThreadEventExecutor.this.run();而這是SingleThreadEventExecutor的一個抽象方法,實(shí)際上會調(diào)用NioEventLoop類的run()方法,是的我們又回到了NioEventLoop類中,這是一個很重要的方法。
NioEventLoop.run() ———— 事件循環(huán):
該方法通過Java NIO Selector的多路復(fù)用來實(shí)現(xiàn)對多個Channel的監(jiān)控,該方法還對epoll空輪詢bug進(jìn)行了解決,并且在處理完Selector返回的可執(zhí)行事件后,會處理taskQueue中的任務(wù)以及定時或周期性任務(wù)。注意,這里不要混淆了,selector完成的是我們注冊的Channel所感興趣的的讀/寫事或者acceptor、connect事件是否已經(jīng)可以處理,這些都是會造成堵塞的I/O事件,是會通過操作系統(tǒng)底層來通知我們事件可執(zhí)行了。而taskQueue中的任務(wù)是我們程序通過execute方法提交的任務(wù),同時可以執(zhí)行的定時任務(wù)或周期性任務(wù)也會被先放入taskQueue中,然后EventLoop關(guān)聯(lián)的線程再從taskQueue中依次取出任務(wù)來執(zhí)行。
比如,我們的啟動流程中serverBootstrap.bind(5566),該方法中涉及到注冊操作(即,將構(gòu)建好的Channel注冊到EventLoop上),執(zhí)行該注冊操作的線程不是NioEventLoop所關(guān)聯(lián)的線程,此時這個操作會被封裝為一個任務(wù)放入taskQueue中,然后NioEventLoop.run()方法就會從taskQueue中取出該任務(wù):完成已經(jīng)構(gòu)建好ServerSocketChannel注冊到NioEventLoop中的Selector上。然后在綁定操作中就會將ServerSocketChannel和對應(yīng)端口進(jìn)行綁定,并注冊該ServerSocketChannel感興趣的事件為SelectionKey.OP_ACCEPT。那么,在這之后,我們就會通過Selector來實(shí)現(xiàn)對ACCEPT事件的監(jiān)控。
好了關(guān)于NioEventLoop絮絮叨叨了這么多,總的來說,目前我們只要知道:
① NioEventLoop是一個基于JDK NIO的異步事件循環(huán)類,它負(fù)責(zé)處理一個Channel的所有事件在這個Channel的生命周期期間。
② NioEventLoop的整個生命周期只會依賴于一個單一的線程來完成。一個NioEventLoop可以分配給多個Channel,NioEventLoop通過JDK Selector來實(shí)現(xiàn)I/O多路復(fù)用,以對多個Channel進(jìn)行管理。
③ 如果調(diào)用Channel操作的線程是EventLoop所關(guān)聯(lián)的線程,那么該操作會被立即執(zhí)行。否則會將該操作封裝成任務(wù)放入EventLoop的任務(wù)隊(duì)列中。
④ 所有提交到NioEventLoop的任務(wù)都會先放入隊(duì)列中,然后在線程中以有序(FIFO)/連續(xù)的方式執(zhí)行所有提交的任務(wù)。
⑤ NioEventLoop的事件循環(huán)主要完成了:a)已經(jīng)注冊到Selector的Channel的監(jiān)控,并在感興趣的事件可執(zhí)行時對其進(jìn)行處理;b)完成任務(wù)隊(duì)列(taskQueue)中的任務(wù),以及對可執(zhí)行的定時任務(wù)和周期性任務(wù)的處理(scheduledTaskQueue中的可執(zhí)行的任務(wù)都會先放入taskQueue中后,再從taskQueue中依次取出執(zhí)行)。
關(guān)于 EventLoop 和 EventLoopGroup
Q:筆者起初在查看EventLoop和EventLoopGroup的源碼時,有一個困惑,我想大家也許和我會有這一樣的困惑:正如我們前面所說的,EventLoopGroup維護(hù)一組EventLoop,可以通過EventLoopGroup的next()方法來獲取EventLoop來使用。但為什么EventLoop又要反過來繼承EventLoopGroup了?
A:是這樣的,Netty在將一個Channel注冊到EventLoop的時候,首先會調(diào)用EventLoopGroup實(shí)現(xiàn)類的register方法來從EventLoopGroup中得到一個EventLoop,然后在調(diào)用EventLoop的register方法來真正完成將Channel注冊到這個EventLoop上。而register方法是聲明在EventLoopGroup接口類中,所以EventLoop繼承了EventLoopGroup。
關(guān)于NioEventLoop和OioEventLoop
Netty使用了一個公共的API層,該API涵蓋了所以的傳輸實(shí)現(xiàn),包括了OIO、NIO、Local transport和Embedded transport。
關(guān)于OIO傳輸,Channel和EventLoop的關(guān)系是一一對應(yīng)的。因此在構(gòu)建EventLoopGroup時不會提前構(gòu)建好EventLoop,而是在每次執(zhí)行注冊Channel操作的時候,才創(chuàng)建一個EventLoop(如果有空閑的EventLoop的話會先使用空閑的EventLoop,而空閑的EventLoop來自于某個Channel從EventLoop上注銷后,使得該EventLoop不再于任何Channel關(guān)聯(lián)而處于空閑狀態(tài)),并將該Channel注冊到該EventLoop上。并且在OIO傳輸模式下,是不支持通過EventLoopGroup的next()方法來獲取EvetLoop的,該方法是適用于提前在EventLoopGroup中構(gòu)建好已知數(shù)量的EventLoop的。如前面所說,OIO傳輸模式下EventLoop與Channel是一一對應(yīng)的,因此EventLoop的數(shù)量是無法預(yù)知的。源碼如下:
OioEventLoopGroup是OIO傳輸模式使用的EventLoopGroup的實(shí)現(xiàn)類,而OioEventLoopGroup繼承了ThreadPerChannelEventLoopGroup
關(guān)于NIO傳輸,Channel和EventLoop是多對一的關(guān)系,也就是可以將一個EventLoop分配給多個Channel。在這種模式下,EventLoop的個數(shù)是有限的,并且會在構(gòu)建EventLoopGroup對象的時候就將相應(yīng)數(shù)目(默認(rèn)為操作系統(tǒng)可運(yùn)行處理器數(shù)的2倍)的EventLoop對象創(chuàng)建出來。并且在每次注冊Channel的時候,以輪詢的方式獲取已經(jīng)構(gòu)建好的EventLoop對象,并將Channel注冊到這個EventLoop上。源碼如下:
NioEventLoopGroup是NIO傳輸模式使用的EventLoopGroup的實(shí)現(xiàn)類,而NioEventLoopGroup繼承了MultithreadEventLoopGroup
ServerBootstrap
ServerBootstrap它使得我們可以輕松地去啟動ServerChannel。ServerChannel:
ServerChannel是一個Channel,它會試圖接受另一端發(fā)過來的連接請求并且通過接受這個請求來創(chuàng)建它的child Channel。child Channel表示真正與客戶端連接的Channel。
服務(wù)端通過ServerBootstrap來引導(dǎo)啟動,而客戶端通過Bootstrap來引導(dǎo)啟動。AbstractBoostrap處理兩個種類應(yīng)用的共同引導(dǎo)步驟,然后特定的步驟客戶端由Bootstrap、服務(wù)端有ServerBootstrap分別處理。
Q:為什么AbstractBoostrap類是可克隆的?
A:在某些時候你需要創(chuàng)建許多相似或完全相同設(shè)置的channels。為了支持在無需創(chuàng)建一個新的bootstrap實(shí)例并能配置每個channel的模式,AbstractBootstrap被標(biāo)志為Cloneable。在一個已經(jīng)配置好的bootstrap上調(diào)用clone()方法將返回另一個可立即使用的boostrap實(shí)例。
注意,這里創(chuàng)建的只是一個bootstrap的EventLoopGroup的淺拷貝,所以這個EventLoopGroup會被所有拷貝生成的channels共享。這是可以理解的,作為拷貝而來的channel經(jīng)常都是短暫的,一個典型的場景是一個channel的創(chuàng)建用于生成一個HTTP請求。
源碼解析
NioEventLoopGroup的構(gòu)建
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
前面我們已經(jīng)對NioEventLoop的結(jié)構(gòu)和作用進(jìn)行了介紹,這里我們直接跟到NioEventLoop構(gòu)造方法的底層實(shí)現(xiàn)進(jìn)行分析。
NioEventLoopGroup構(gòu)造方法最終會調(diào)用如下方法:
傳入的參數(shù)詳解:
-
int nThreads :將使用在該實(shí)例上的線程個數(shù)。
傳入的值為:默認(rèn)構(gòu)造方法中,該參數(shù)的值為MultithreadEventLoopGroup類中定義DEFAULT_EVENT_LOOP_THREADS常量值。
① 如果系統(tǒng)屬性中有“io.netty.eventLoopThreads”屬性值,則返回;否則取可用處理器核心 * 2(處理器超線程數(shù)) * 2的值(即,NettyRuntime.availableProcessors() * 2)。
② 取第①步結(jié)果與1中大的那個值。
這里簡單補(bǔ)充下處理器核心數(shù)、超線程等的概念:
總核數(shù) = 物理CPU個數(shù) X 每顆物理CPU的核數(shù)
總邏輯CPU數(shù) = 物理CPU個數(shù) X 每顆物理CPU的核數(shù) X 超線程數(shù)(超線程數(shù)就是2,但前提得是支持超線程) - Executor executor :使用的執(zhí)行器,如果為“null”,則表示使用默認(rèn)的執(zhí)行器
傳入的值為:null - EventExecutorChooserFactory chooserFactory :EventExecutor選擇器工廠
傳入的值為:DefaultEventExecutorChooserFactory.INSTANCE
DefaultEventExecutorChooserFactory通過簡單的輪詢來獲取下一個EventExecutor。 -
SelectorProvider selectorProvider :Selector選擇器
傳入的值為:SelectorProvider.provider();
① 如果配置了“java.nio.channels.spi.SelectorProvider”屬性,則通過該屬性值load對應(yīng)的SelectorProvider對象,如果構(gòu)建失敗則拋異常。
② 如果provider類已經(jīng)安裝在了對系統(tǒng)類加載程序可見的jar包中,并且該jar包的源碼目錄META-INF/services包含有一個java.nio.channels.spi.SelectorProvider提供類配置文件,則取文件中第一個類名進(jìn)行l(wèi)oad以構(gòu)建對應(yīng)的SelectorProvider對象,如果構(gòu)建失敗則拋異常。
③ 如果上面兩種情況都不存在,則返回系統(tǒng)默認(rèn)的SelectorProvider,即,sun.nio.ch.DefaultSelectorProvider.create();
④ 隨后在調(diào)用該方法,即SelectorProvider.provider()。則返回第一次調(diào)用的結(jié)果。 - SelectStrategyFactory selectStrategyFactory :選擇策略工廠
傳入的值為:DefaultSelectStrategyFactory.INSTANCE
DefaultSelectStrategyFactory:使用了默認(rèn)選擇策略的工廠。 -
RejectedExecutionHandler rejectedHandler :拒絕執(zhí)行器
傳入的值為:RejectedExecutionHandlers.reject(),該方法返回一個REJECT常量實(shí)例
方法體詳解:
① executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()):構(gòu)建默認(rèn)的執(zhí)行器。并且傳入的一個默認(rèn)的線程工廠給ThreadPerTaskExecutor。
ThreadPerTaskExecutor繼承了Executor,它會使用newDefaultThreadFactory()所返回的線程工廠來創(chuàng)建線程。
“命令模式”:execute(Runnable command)這里使用到了命令模式,任務(wù)的請求和執(zhí)行分離開來了。
“代理模式”:這里execute方法實(shí)現(xiàn)并不是由ThreadPerTaskExecutor來執(zhí)行的,而是交由ThreadFactory來執(zhí)行的。ThreadFactory是通過構(gòu)造方法傳遞進(jìn)來的。
② children = new EventExecutor[nThreads]:根據(jù)傳入了的nThreads為數(shù)組長度,創(chuàng)建EventExecutor數(shù)組對象,將該數(shù)組對象賦值給MultithreadEventExecutorGroup成員屬性children。
③ 對children數(shù)組元素進(jìn)行初始化,即構(gòu)建一個個NioEventLoop實(shí)例:children[i] = newChild(executor, args);主要完成了對NioEventLoop類的創(chuàng)建。NioEventLoop構(gòu)造函數(shù)中完成了NioEventLoop中的Selector的啟用(即,provider.openSelector());以及對EventLoop類中幾個成員屬性的設(shè)置,包括SelectorProvider ———— Selector提供器、selector ———— 封裝后的Selector、unwrappedSelector ———— 未封裝的Selector、selectStrategy ———— 選擇策略類。構(gòu)建成員屬性tailTasks、taskQueue,兩隊(duì)列均為MpscUnboundedArrayQueue實(shí)例。設(shè)置成員屬性addTaskWakesUp為false。
④ chooser = chooserFactory.newChooser(children):設(shè)置EventExecutorChoose ———— 事件執(zhí)行器選擇器。該選擇器會以輪詢的方式從上面構(gòu)建好的EventExecutor[] children數(shù)組中獲取EventExecutor。??這里當(dāng)EventExecutor[]數(shù)組長度是2^N時使用PowerOfTwoEventExecutorChooser選擇器,否則使用GenericEventExecutorChooser。
這兩個選擇器都是實(shí)現(xiàn)了以輪詢的方式從EventExecutor[]數(shù)組中依次獲取EventExecutor來使用,不同的地方在于它們實(shí)現(xiàn)的細(xì)節(jié)上。PowerOfTwoEventExecutorChooser因?yàn)閿?shù)組長度是2^N,因此使用了’&’位的與操作來計(jì)算應(yīng)該獲取的EventExecutor在數(shù)組中的索引值。而GenericEventExecutorChooser則是通過’%’取模運(yùn)算得到應(yīng)該獲取的EventExecutor在數(shù)組中的索引值。當(dāng)然,’&’運(yùn)算性能是高于’%’運(yùn)算的。
⑤ 為EventExecutor[] children數(shù)組中的EventExecutor設(shè)置終止操作的監(jiān)聽事件。
該終止事件會發(fā)生在,當(dāng)前EventLoopGroup所管理的所有EventLoop都終止后。
⑥ 根據(jù)EventExecutor[] children數(shù)組構(gòu)建一個不可修改的集合,并賦值給MultithreadEventExecutorGroup的成員屬性readonlyChildren。
啟動類設(shè)置
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new MyServerInitializer());
ServerBootstrap是netty提供給我們的輔助類而已,它本身也沒有做任何多余的事情,只用來設(shè)定一些相關(guān)的屬性、參數(shù)。
serverBootstrap.group(bossGroup, workerGroup):
這個EventLoopGroup用于處理將要創(chuàng)建的那個Channel的所有的事件。
該方法會將我們傳進(jìn)來的bossGroup賦值給AbstractBootstrap的成員變量volatile EventLoopGroup group。
總的來說:
serverBootstrap.group(bossGroup, workerGroup)完成了將傳進(jìn)來的兩個EventLoopGroup對象分別給ServeBootstrap的父類AbstractBootstrap和ServerBootstrap本身的兩個成員變量賦值。也就是說,我們前面創(chuàng)建的兩個NioEventLoopGroup在ServerBootstrap中有相應(yīng)的引用去指向這個具體的實(shí)例。
在AbstractBootstrap中保持的是一個bossGroup的成員變量;而workerGroup是在ServerBootstrap中持有的一個成員變量。
parentGroup(即,bossGroup)的作用就是接收遠(yuǎn)端發(fā)過來的連接,它不處理這個連接,它把處理的任務(wù)丟給childGroup(即,workerGroup)來完成。
serverBootstrap.channel(NioServerSocketChannel.class):
這個Class(??傳入的參數(shù))用于創(chuàng)建一個Channel的實(shí)例。你可以使用該方法(即,channel(Class<? extends C> channelClass))或者使用channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory)方法如果Channel實(shí)現(xiàn)沒有無參的構(gòu)造方法。
也就是說使用該方法構(gòu)建的Channel是具有無參構(gòu)造方法的,否則就要用channelFactory(...)方法。
它是一個ChannelFactory,它會通過以反射的形式調(diào)用Channel默認(rèn)的構(gòu)造方法來實(shí)例化一個新的Channel。
channelFactory(new ReflectiveChannelFactory<C>(channelClass)):將channelFactory參數(shù)設(shè)置給AbstractBootstrap的成員變量channelFactory。
總的來說:
serverBootstrap.channel(NioServerSocketChannel.class)完成了將NioServerSocketChannel的ChannelFactory賦值給AbstractBootstrap的成員變量channelFactory。該ChannelFactory是一個ReflectiveChannelFactory實(shí)例,它能夠通過以反射的形式調(diào)用Channel默認(rèn)的構(gòu)造方法來實(shí)例化一個新的Channel。
serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)):
將我們傳入的LoggingHandler賦值給AbstractBootstrap的成員變量handler。handler用于ServerChannel(即,NioServerSocketChannel)上。
serverBootstrap.childHandler(new MyServerInitializer()):
將我們傳入的MyServerInitializer賦值給ServerBootstrap的成員變量childHandler。childHandler用于child Channel(即,NioSocketChannel)上。
在配置啟動類的過程中需要注意兩點(diǎn):
① Channel 和 EventLoopGroup 的兼容性
EventLoopGroup 和 Channel 都實(shí)現(xiàn)了 NIO 和 OIO 的傳輸。
② 必須設(shè)定的配置
在你調(diào)用bing()或connect()方法前下面的方法必須被調(diào)用,否則將會觸發(fā)IllegalStateException異常。以下是必須要設(shè)置的組件(下面組件的設(shè)置并沒有任何順序要求)。
a) group()
b) channel() or channelFactory()
c) handler() or childHandler()
總結(jié):
到目前為止我們已經(jīng)對??四步程序代碼進(jìn)行了詳細(xì)的解析
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new MyServerInitializer());
這四步代碼主要完成了:
① NioEventLoopGroup的構(gòu)建。在NIO模式中我們會在構(gòu)建EventLoopGroup的時候就將EventLoop一并的給構(gòu)建處理。因?yàn)樵贜IO模式中,EventLoop和Channel的關(guān)系是一對多的關(guān)系,多個Channel可以注冊到同一個EventLoop中,因此每個EventLoopGroup中的EventLoop的個數(shù)是固定的,而在OIO模式下,我們就無法在構(gòu)建EventLoopGroup的同時將EventLoop也構(gòu)建出來了。因?yàn)樵贠IO模式中,EventLoop和Channel是一對一的關(guān)系,每個Channel都會注冊到一個唯一的EventLoop中。因此在OIO模式中,EventLoop的個數(shù)隨著Channel的增加而增加,而EventLoop的構(gòu)建也放到了將Channel注冊到EventLoop時進(jìn)行。
而造成上面實(shí)現(xiàn)不同的本質(zhì)原因還是應(yīng)用傳輸協(xié)議模式的不同。NIO模式是非阻塞模式,底層使用了Selector來通過更少的線程同時管理大量的連接;而OIO模式是阻塞模式,每一個連接都需要一個線程來處理。
② 通過ServerBootstrap對服務(wù)的配置進(jìn)行設(shè)置。其中a) group();b) channel() or channelFactory();c) childHandler() 配置是不可缺少的。在上面的源碼解析中,我們可以總結(jié)出,涉及到對child Channel(即,服務(wù)端接收客戶端的請求連接后生產(chǎn)的child Channel,該child Channel就是真正和客戶端連接的Channel)的配置都會設(shè)置在ServerBootstrap中,比如childGroup、childHandler;而AbstractBootstrap中則保持了ServerChannel相關(guān)的配置,比如bossGroup、handle、channelFactory。
目前為止已經(jīng)將ServerBootstrap所必須的成員變量都設(shè)置好了。但僅僅是配置而已,并未對ServerChannel進(jìn)行構(gòu)建等等。這些都是在bind操作會觸發(fā)的。下一篇我們就會解析啟動流程中的bind操作。
后記
本文主要對Netty服務(wù)端的啟動流程源碼進(jìn)行了部分的解析。建議大家可以看看 Netty in action ——— 異步和事件驅(qū)動、Netty in action ——— Bootstrapping、Netty in action ——— 事件循環(huán) 和 線程模式、Netty in action ——— 傳輸協(xié)議這幾篇文章,這幾篇文章是在筆者寫源碼解析時涉及到的一些知識點(diǎn)的理論性文章,主要來自于《Netty in action》一書。
若文章有任何錯誤,望大家不吝指教:)
參考
圣思園《精通并發(fā)與Netty》