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中內部類AbstractUnsafe的register()方法, 具體如何調用到這個方法, 能夠簡單帶你們捋一下ide
首先看下config()方法, 因爲是ServerBootstrap調用的, 因此咱們跟進去:oop
public final ServerBootstrapConfig config() { return config; }
返回的config是ServerBootrap的成員變量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(); }
這裏調用Bootstrap的group()方法:
public final EventLoopGroup group() { return group; }
這裏返回了AbstractBootstrap的成員變量group, 咱們回顧下第一小節, 還記得AbstractBootstrap的group(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的父類的MultithreadEventLoopGroup的register()方法
跟到MultithreadEventLoopGroup的register()方法:
public ChannelFuture register(Channel channel) { return next().register(channel); }
這裏的代碼看起來有點暈, 不要緊, 之後會講到, 如今能夠大概作個瞭解, NioEventLoopGroup是個線程組, 而next()方法就是從線程組中選出一個線程, 也就是NioEventLoop線程, 因此這裏的next()方法返回的是NioEventLoop對象, 其中register(channel)最終會調用NioEventLoop的父類SingleThreadEventLoop的register(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對象
跟進去看AbstractChannel的unsafe()的實現:
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顧名思義就是不安全的, 由於不少對channel的io方法都定義在unsafe中, 因此netty將其做爲內部類進行封裝, 防止被外部直接調用, unsafe接口是Channel接口的內部接口, unsafe的子類也分別封裝在Channel的子類中, 好比咱們如今剖析的register()方法, 就是封裝在AbstractChannel類的內部類AbstractUnsafe中的方法, 有關Unsafe和Channel的繼承關係以下:
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;
eventLoop是AbstractChannel的成員變量, 有關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()最終會調用AbstractNioChannel的doRegister()方法:
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, 由於鏈路沒完成
本小節梳理了有註冊多路複用的相關邏輯, 同窗們能夠跟着代碼本身走一遍以加深印象