Netty源碼分析第1章(Netty啓動流程)---->第4節: 註冊多路複用

 

Netty源碼分析第一章:Netty啓動流程html

 

第四節:註冊多路複用java

 

回顧下以上的小節, 咱們知道了channel的的建立和初始化過程, 那麼channel是如何註冊到selector中的呢?咱們繼續分析bootstrap

回到上一小節的代碼:promise

final ChannelFuture initAndRegister() { Channel channel = null; try { //建立channel
        channel = channelFactory.newChannel(); //初始化channel
 init(channel); } catch (Throwable t) { //忽略非關鍵代碼
 } //註冊channel
    ChannelFuture regFuture = config().group().register(channel); //忽略非關鍵代碼
    return regFuture; }

咱們講完建立channel和初始化channel的關鍵步驟, 咱們繼續跟註冊channel的步驟:安全

ChannelFuture regFuture = config().group().register(channel);

其中, 重點關注下register(channel)這個方法, 這個方法最終會調用到AbstractChannel中內部類AbstractUnsaferegister()方法, 具體如何調用到這個方法, 能夠簡單帶你們捋一下ide

首先看下config()方法, 因爲是ServerBootstrap調用的, 因此咱們跟進去:oop

public final ServerBootstrapConfig config() { return config; }

返回的configServerBootrap的成員變量config:源碼分析

private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);

 

 

跟到ServerBootstrapConfig的構造方法:this

ServerBootstrapConfig(ServerBootstrap bootstrap) { super(bootstrap); }

繼續跟到其父類AbstractBootstrapConfig的構造方法:spa

protected AbstractBootstrapConfig(B bootstrap) { this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap"); }

咱們發現咱們建立的ServerBootstrap做爲參數初始化了其成員變量bootstrap

 

回到initAndRegister()方法:

config()返回的是ServerBootstrapConfig對象

再繼續跟到group()方法:

public final EventLoopGroup group() { return bootstrap.group(); }

這裏調用Bootstrapgroup()方法:

public final EventLoopGroup group() { return group; }

這裏返回了AbstractBootstrap的成員變量group, 咱們回顧下第一小節, 還記得AbstractBootstrapgroup(EventLoopGroup group)方法嗎?

public B group(EventLoopGroup group) { this.group = group; return (B) this; }

group(EventLoopGroup group)方法初始化了咱們boss線程, group()返回了boss線程, 也就是說 config().group().register(channel) 中的register()方法是boss線程對象調用的, 因爲咱們當初初始化的是NioEventLoopGroup, 所以走的是NioEventLoopGroup的父類的MultithreadEventLoopGroupregister()方法

跟到MultithreadEventLoopGroup的register()方法:

public ChannelFuture register(Channel channel) { return next().register(channel); }

這裏的代碼看起來有點暈, 不要緊, 之後會講到, 如今能夠大概作個瞭解, NioEventLoopGroup是個線程組, next()方法就是從線程組中選出一個線程, 也就是NioEventLoop線程, 因此這裏的next()方法返回的是NioEventLoop對象, 其中register(channel)最終會調用NioEventLoop的父類SingleThreadEventLoopregister(channel)方法

跟到SingleThreadEventLoop的register(channel)方法:

public ChannelFuture register(Channel channel) { return register(new DefaultChannelPromise(channel, this)); }

其中DefaultChannelPromise類咱們以後也會講到

 

咱們先跟到register(new DefaultChannelPromise(channel, this)):

public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNull(promise, "promise"); promise.channel().unsafe().register(this, promise); return promise; }

channel()會返回咱們初始化的NioServerSocketChannel, unsafe()會返回咱們建立channel的時候初始化的unsafe對象

跟進去看AbstractChannelunsafe()的實現:

public Unsafe unsafe() { return unsafe; }

這裏返回的unsafe, 就是咱們初始化channel建立的unsafe

回顧下第二小節channel初始化的步驟:

protected AbstractChannel(Channel parent) { this.parent = parent; id = newId(); unsafe = newUnsafe(); pipeline = newChannelPipeline(); }

咱們看unsafe的初始化:unsafe=newUnsafe()

 

跟到newUnsafe(), 咱們以前講過NioServerSokectChannel的父類是AbstractNioMessageChannel, 因此會調用到到AbstractNioMessageChannel類中的newUnsafe()

跟到AbstractNioMessageChannel類中的newUnsafe():

protected AbstractNioUnsafe newUnsafe() { return new NioMessageUnsafe(); }

咱們看到這裏建立了NioMessageUnsafe()對象, 因此在 promise.channel().unsafe().register(this, promise) 代碼中, unsafe()是返回的NioMessageUnsafe()對象, 最後調用其父類AbstractUnsafe(也就是AbstractChannel的內部類)register()方法,

 

簡單介紹下unsafe接口, unsafe顧名思義就是不安全的, 由於不少對channelio方法都定義在unsafe, 因此netty將其做爲內部類進行封裝, 防止被外部直接調用, unsafe接口是Channel接口的內部接口, unsafe的子類也分別封裝在Channel的子類中, 好比咱們如今剖析的register()方法, 就是封裝在AbstractChannel類的內部類AbstractUnsafe中的方法, 有關UnsafeChannel的繼承關係以下:

1-4-1

以上內容若是不明白沒有關係, 有關NioEventLoop相關會在後面的章節講到, 目前咱們只是瞭解是如何走到AbstractUnsafe類的register()便可

 

咱們繼續看看register()方法:

public final void register(EventLoop eventLoop, final ChannelPromise promise) { //代碼省略 //全部的複製操做, 都交給eventLoop處理(1)
    AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) { register0(promise); } else { try { eventLoop.execute(new Runnable() { @Override public void run() { //作實際主註冊(2)
 register0(promise); } }); } catch (Throwable t) { //代碼省略
 } } }

咱們跟着註釋的步驟繼續走, 第一步, 綁定eventLoop線程:

AbstractChannel.this.eventLoop = eventLoop;

eventLoopAbstractChannel的成員變量, 有關eventLoop, 咱們會在緒章節講到, 這裏咱們只須要知道, 每一個channel綁定惟一的eventLoop線程, eventLoop線程和channel的綁定關係就是在這裏展示的

 

再看第二步, 作實際註冊:

咱們先看if判斷, if(eventLoop.inEventLoop())

 

這裏是判斷是否是eventLoop線程, 顯然咱們如今是main()方法所在的線程, 因此走的else, eventLoop.execute()是開啓一個eventLoop線程, register0(promise)就是再開啓線程以後, 經過eventLoop線程執行的, 這裏你們暫時做爲了解

 

咱們重點關注register0(promise), 跟進去:

private void register0(ChannelPromise promise) { try { //作實際的註冊(1)
 doRegister(); neverRegistered = false; registered = true; //觸發事件(2)
 pipeline.invokeHandlerAddedIfNeeded(); safeSetSuccess(promise); //觸發註冊成功事件(3)
 pipeline.fireChannelRegistered(); if (isActive()) { if (firstRegistration) { //傳播active事件(4)
 pipeline.fireChannelActive(); } else if (config().isAutoRead()) { beginRead(); } } } catch (Throwable t) { //省略代碼
 } }

咱們重點關注doRegister()這個方法

 

doRegister()最終會調用AbstractNioChanneldoRegister()方法:

protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { //jdk底層的註冊方法 //第一個參數爲selector, 第二個參數表示不關心任何事件
            selectionKey = javaChannel().register(eventLoop().selector, 0, this); return; } catch (CancelledKeyException e) { //省略代碼
 } } }

咱們終於看到和java底層相關的方法了

跟到javaChannel()的方法中:

protected SelectableChannel javaChannel() { return ch; }

這個ch, 就是本章第二小節建立NioServerSocketChannel中初始化的jdk底層ServerSocketChannel

這裏register(eventLoop().selector, 0, this)方法中eventLoop().selector, 是得到每個eventLoop綁定的惟一的selector, 0表明此次只是註冊, 並不監放任何事件, this是表明將自身(NioEventLoopChannel)做爲屬性綁定在返回的selectionKey當中, 這個selectionKey就是與每一個channel綁定的jdk底層的SelectionKey對象, 熟悉nio的小夥伴應該不會陌生, 這裏再也不贅述

 

回到register0(ChannelPromise promise)方法, 咱們看後續步驟:

步驟(2)是觸發handler的須要添加事件, 事件傳遞的內容咱們將在後續課程詳細介紹, 這裏沒必要深究

步驟(3)是觸發註冊成功事件(3), 同上

步驟(4)是傳播active事件(4), 這裏簡單強調一下, 這裏的方法pipeline.fireChannelActive()第一個註冊是執行不到的, 由於isActive()會返回false, 由於鏈路沒完成

本小節梳理了有註冊多路複用的相關邏輯, 同窗們能夠跟着代碼本身走一遍以加深印象

 

上一節: 服務端Channel的初始化

下一節: 綁定端口

相關文章
相關標籤/搜索