上一章 中的標準netty啓動代碼中,ServerBootstrap究竟是如何啓動的呢?這一章咱們來瞅下。java
server.group(bossGroup, workGroup); server.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100); server.childHandler(new DealNettyServerInitializer()); ChannelFuture future = server.bind(7878).sync();
啓動代碼無非這麼幾行,我一行一行的瞅。服務器
server.group(bossGroup, workGroup);socket
還記得上一章定義的兩個NioEventLoopGroup不?bossGroup和workGroup。這裏的ServerBootstrap的group方法就是將workGroup直接賦值給了該對象的childGroup。而bossGroup傳到了其父類AbstractBootstrap中,進行保存。oop
看到這,咱們知道了兩個類的關係:ServerBootstrap是AbstractBootstrap的子類。this
server.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100);spa
會將NioServerSocketChannel的class對象做爲參數構建ReflectiveChannelFactory對象。這是什麼對象呢?進去看一下:.net
哦,原來是一個經過反射構建對象的工廠類。回到上一步,netty會把ReflectiveChannelFactory對象做爲參數調用channelFactory()方法。進入這個方法看一下:3d
原來只是保存ReflectiveChannelFactory對象。注意,channel方法是屬於AbstractBootstrap對象的,所以,ReflectiveChannelFactory對象是保存在AbstractBootstrap對象中的。rest
一樣,option方法也是AbstractBootstrap對象的,options會保存該option對象,那這麼說,options應該是一個集合了,netty
果真是一個map集合。
server.childHandler(new DealNettyServerInitializer());
childHandler是屬於ServerBootstrap類的,因此將DealNettyServerInitializer對象賦值給了ServerBootstrap的childHandler成員變量。
至此,只是保存變量,沒有使用。看下server.bind(7878)方法,此方法就是服務器啓動的真正入口。
很簡單,繼續跟進,
調用doBind方法,繼續跟進:
這麼多代碼,主要是initAndRegister方法,所以跟進去。
下面正式進入啓動流程,啓動流程大致分爲4步:
一、建立服務端channel:NioServerSocketChannel
二、初始化服務端Channel:NioServerSocketChannel
三、註冊Selector:將Channel註冊到Selector上
四、端口的綁定:服務端端口的監聽。
下面,一步一步的分析:
一、建立服務端channel:NioServerSocketChannel
忘了說了,這個bind方法是調用的AbstractBootstrap的方法哦,所以這個channelFactory就是channel()方法的參數ReflectiveChannelFactory,下圖爲證:
還記的ReflectiveChannelFactory的newChannel方法嗎?
很明顯,就是經過反射構建NioServerSocketChannel。由於咱們傳入的是NioServerSocketChannel的Class對象。
NioServerSocketChannel對象是生成了,咱們一塊兒看一下這個NioServerSocketChannel的構造方法吧。
這個newSocket是作了什麼?
哦,原來是調用jdk的代碼,生成Nio的ServerSocketChannel呀。繼續查看:
跟進this:
記住,這裏的第三個參數是accept事件,,後續咱們還會提到。跟進父類的構造方法:
咱們能夠看到,NioServerSocketChannel的父類是AbstractNioMessageChannel。而後這個構造方法只是繼續調用父類的構造方法。
咱們又知道了,AbstractNioMessageChannel的父類是AbstractNioChannel。而且剛纔說注意的accept事件,賦值給了AbstractNioChannel的成員變量readInterestOp,後續還會遇到。
注意這個方法,ch是什麼?就是傳進來的jdk原生的ServerSocketChannel,一次該方法明顯是將此通道將被置於非阻塞模式。
繼續跟進父類的構造方法:
咱們又知道了AbstractNioChannel的父類是AbstractChannel。而且,在該構造方法中,實例化了id,unsafe和pipeline。
明顯此處的NioMessageUnsafe的父類是AbstracNioUnsafe。
到這裏,一系列的父類構造方法的調用結束了。咱們先總結下他們之間的關係。
NioServerSocketChannel的父類是AbstractNioMessageChannel;AbstractNioMessageChannel的父類是AbstractNioChannel;AbstractNioChannel的父類是AbstractChannel;還有一個額外的關係
NioMessageUnsafe的父類是AbstractNioUnsafe。
即:
NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel;
NioMessageUnsafe -> AbstractNioUnsafe
爲啥非要強調這種繼承關係?由於後續的調用很複雜,若是不記住他們的關係,很容易分不清楚變量是從何 而來的。。
好了,我再回到開始的NioServerSocketChannel的構造方法上:
看過了super一系列的調用,咱們瞅下下面的代碼。先看下javaChannel()幹了什麼?
調用了父類的javaChannel方法。NioServerSocketChannel的父類是AbstractNioMessageChannel,所以看AbstractMessageNioChannel類的javaChannel方法,咱們發現AbstractMessageNioChannel類中並無javaChannel方法,所以應該調用的是AbstractNioMessageChannel的父類AbstractNioChannel的javaChannel,咱們看一下:
這個ch,不就是以前傳入的jdk原聲的ServerSocketChannel對象麼。。。圖下證:
所以javaChannel方法,獲取的是原生的ServerSocketChannel對象。點個贊,這個方法的名字真是見名知意呀。那javaChannel().socket()咱們就很容易知道了,就是打開一個socket鏈接。並將該鏈接封裝到NioServerSocketChannelConfig對象中保存在NioServerSocketChannel的config成員變量上。至此,建立服務端channel:NioServerSocketChannel完成。
二、初始化服務端Channel:NioServerSocketChannel
回到initAndRegister方法,看完了NioServerSocketChannel的實例化,咱們看下緊接着的代碼init():
進入init方法:
哎呦,這個又一坨代碼。。。。不要緊,不少代碼都很簡單:
上面代碼很簡單,就是setChannelOptions和setChannelAttrs。
同理,setChildOptions和setChildAttrs。
首先聲明下,initChannel方法不是當即被調用的哦,後面會講到什麼時候被調用。總之會被調用,但不是這裏。
p是啥,,這個p就是NioServerSocketChannel裏成員變量,準確的說是父類父類父類的成員變量,即AbstractChannel的成員變量。下圖證:
那個initChannel的方法裏的pipeline變量也是p。那pipeline是啥呢?之後會講的,這裏只要知道它是一個雙向鏈表結構就能夠了。
所以這段的意思是將handler對象放入到pipeline中。
看到這裏,一下懵了,NioServerSocketChannel裏面啥時候實例化的EventLoop對象呀?說明下,此刻尚未實例化,由於沒有調用該方法,因此暫時不會又null異常。
這段的邏輯就是向pipeline中添加鏈接器ServerBootstrapAcceptor。
好了,今天先到這,下面兩步代碼太深,明天再繼續吧。最後,必定要記住繼承的關係。