Netty 系列目錄(http://www.javashuo.com/article/p-hskusway-em.html)html
chnnel 初始化完成後就須要將其註冊到對應的 NioEventLoop 上。java
(1) NioEventLoopGroup 註冊promise
// NioEventLoopGroup -> MultithreadEventLoopGroup public ChannelFuture register(Channel channel) { return next().register(channel); }
group() 返回的是上文配置的 childGroup 對象,即 NioEventLoopGroup,每一個 NioEventLoopGroup 持有多個 NioEventLoop,next 依次返回一個 NioEventLoop。緩存
(2) NioEventLoop 註冊多線程
// NioEventLoop -> SingleThreadEventLoop public ChannelFuture register(Channel channel) { return register(new DefaultChannelPromise(channel, this)); } public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNull(promise, "promise"); promise.channel().unsafe().register(this, promise); return promise; }
能夠看到最終調用對應 channel 的 unsafe 進行註冊,下面看一下 unsafe 是如何註冊的。app
(3) Unsafe 註冊ide
NioServerSocketChannel 的 unsafe = new NioMessageUnsafe(),而 NioMessageUnsafe 繼承自 AbstractUnsafe。oop
// NioServerSocketChannel -> AbstractChannel.AbstractUnsafe public final void register(EventLoop eventLoop, final ChannelPromise promise) { // 省略... AbstractChannel.this.eventLoop = eventLoop; // 同一個 channel 的註冊、讀、寫等都在 eventLoop 完成,避免多線程的鎖競爭 if (eventLoop.inEventLoop()) { // 將 channel 註冊到 eventLoop 上 register0(promise); } else { try { eventLoop.execute(new Runnable() { @Override public void run() { register0(promise); } }); } catch (Throwable t) { // 省略... } } } private void register0(ChannelPromise promise) { // 1. 確保 channel 的狀態是 open,最終調用 ch.isOpen() if (!promise.setUncancellable() || !ensureOpen(promise)) { return; } boolean firstRegistration = neverRegistered; // 2. channel 註冊到 eventLoop 上 doRegister(); neverRegistered = false; registered = true; // 3. 此時 channel 已經註冊到 eventLoop 上,此時須要將註冊的 handler 綁定到 channel 上。eg: ChannelInitializer pipeline.invokeHandlerAddedIfNeeded(); safeSetSuccess(promise); pipeline.fireChannelRegistered(); // 4. channel 狀態爲 active 就須要觸發 ChannelActive 事件或準備讀 if (isActive()) { if (firstRegistration) { pipeline.fireChannelActive(); } else if (config().isAutoRead()) { beginRead(); } } }
register 中最重要的方法是調用 doRegister 進行註冊,該方法調用了 JDK 底層的代碼進行註冊。this
(4) doRegister線程
// AbstractNioChannel protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { // 1. 調用 JDK NIO 將 channel 註冊到 eventLoop 的 selector 上 selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this); return; } catch (CancelledKeyException e) { if (!selected) { // 2. 調用 selectNow 將註冊的 channel 移除,否則有可能被緩存沒有刪除 // 移除後繼續註冊 eventLoop().selectNow(); selected = true; } else { // 3. JDK 提供的文檔此時不會出現該異常,實際... JDK bug throw e; } } } }
AbstractNioChannel#javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
register 方法註冊 Java 原生 NIO 的 Channel 對象到 Selector 對象。可是爲何感興趣事件的是 0 呢?正常狀況下,對於服務端來講,須要註冊 SelectionKey.OP_ACCEPT 事件。
(1) 註冊方式是多態的,它便可以被 NIOServerSocketChannel 用來監聽客戶端的鏈接接入,也能夠註冊 SocketChannel 用來監聽冉瑩穎讀或者寫操做。
(2) 經過 SelectionKey#interestOps(int ops) 方法能夠方便地修改監聽操做位。因此,此處註冊須要獲取 SelectionKey 並給 AbstractNIOChannel 的成員變量 selectionKey 賦值。
// AbstractChannel.AbstractUnsafe public final void beginRead() { assertEventLoop(); if (!isActive()) { return; } try { doBeginRead(); } catch (final Exception e) { invokeLater(new Runnable() { @Override public void run() { pipeline.fireExceptionCaught(e); } }); close(voidPromise()); } } // AbstractNioChannel protected void doBeginRead() throws Exception { // Channel.read() or ChannelHandlerContext.read() was called final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; // 從新註冊感興趣的事件類型,readInterestOp 是 channel 初始化的時候傳進來的 final int interestOps = selectionKey.interestOps(); if ((interestOps & readInterestOp) == 0) { selectionKey.interestOps(interestOps | readInterestOp); } }
到此 NioServerSocketChannel 就從新註冊上了 OP_ACCEPT 事件
天天用心記錄一點點。內容也許不重要,但習慣很重要!