Netty源碼學習系列③新連接的建立

當服務端啓動好了之後,也就是說,服務端已經在執行NioEventLoop的一個死循環方法run()中,一直輪詢事件,並且此時的監聽的事件爲OP_ACCEPT。如果有新連接接入,那麼首先會在上述的run()方法中觸發…

收到新的連接

首先,服務端啓動好了之後,會進入等待事件的狀態,也就是調用JDK的NIO的API:

// NioEventLoop.java -> run()
if (!hasTasks()) {
		strategy = select(curDeadlineNanos);
}
// 核心是調用jdk的api
selector.select();

收到新的連接後,將會通過processSelectedKeys()進行處理,處理內容包括:創建、初始化NioSocketChannel

// NioEventLoop.java
private void processSelectedKeys() {
    if (selectedKeys != null) {
        processSelectedKeysOptimized();
    } else {
        processSelectedKeysPlain(selector.selectedKeys());
    }
}

private void processSelectedKeysOptimized() {
    for (int i = 0; i < selectedKeys.size; ++i) {
        final SelectionKey k = selectedKeys.keys[i];
        // 幫助GC
        selectedKeys.keys[i] = null;
				// attachment是NioServerSocketChannel
      	// 服務端註冊selectionKey時傳入
        final Object a = k.attachment();

        if (a instanceof AbstractNioChannel) {
          	// 此處真正進入處理新連接事件
            processSelectedKey(k, (AbstractNioChannel) a);
        } else {
            @SuppressWarnings("unchecked")
            NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
            processSelectedKey(k, task);
        }
        //...
    }
}

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
    final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
    // ...
    try {
      	// 此時的readyOps爲OP_ACCEPT,也就是16,即二進制10000
        int readyOps = k.readyOps();
        // ...

        // 處理新連接的邏輯開始
      	// SelectionKey.OP_READ | SelectionKey.OP_ACCEPT = 10001
        if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
          	// 此處的unsafe與創建連接時的unsafe不是同一個實現
            unsafe.read();
        }
    }
  	// ...
}

創建、初始化NioSocketChannel

上面的unsafe的具體實現是在一個叫做NioMessageUnsafe的內部類中,在它的read方法中:

①創建了NioSocketChannel。②通過pipeline中的Handler,即ServerBootstrap$ServerBootstrapAcceptor中初始化NioSocketChannel。

// 
@Override
public void read() {
    assert eventLoop().inEventLoop();
    final ChannelConfig config = config();
    final ChannelPipeline pipeline = pipeline();
    final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
    allocHandle.reset(config);

    boolean closed = false;
    Throwable exception = null;
    try {
        try {
            do {
              	// ①創建了NioSocketChannel,並加入到readBuf這個List中
                int localRead = doReadMessages(readBuf);
                if (localRead == 0) {
                    break;
                }
                if (localRead < 0) {
                    closed = true;
                    break;
                }

                allocHandle.incMessagesRead(localRead);
            } while (allocHandle.continueReading());
        } catch (Throwable t) {
            exception = t;
        }

        int size = readBuf.size();
        for (int i = 0; i < size; i ++) {
            readPending = false;
          	// ②在ServerBootstrap$ServerBootstrapAcceptor中初始化NioSocketChannel
            pipeline.fireChannelRead(readBuf.get(i));
        }
        readBuf.clear();
        allocHandle.readComplete();
        pipeline.fireChannelReadComplete();
        // ...
    }
  	// ...
}

創建

創建NioSocketChannel的主要流程,就是先通過調用JDK的API獲取SocketChannel,然後再將其作爲一個值傳給NioSocketChannel。因此從另一個方面來看,可以理解成NioSocketChannel是SocketChannel的封裝。

@Override
protected int doReadMessages(List<Object> buf) throws Exception {
  	// 實際調用時serverSocketChannel.accept()  
  	SocketChannel ch = SocketUtils.accept(javaChannel());

    try {
        if (ch != null) {
          	// 創建NioSocketChannel
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    } 
  	// ...
    return 0;
}

初始化

主要依靠pipeline中的相應事件傳遞。比如說,將channel註冊到EventLoop中這個事件,就是靠pipeline中的Handler,ServerBootstrapAcceptor來完成。

// 調用pipeline中的不可覆蓋方法fireChannelRead
pipeline.fireChannelRead(readBuf.get(i));

// 與之前的流程類似,從pipeline中的頭handler開始傳遞事件
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
    AbstractChannelHandlerContext.invokeChannelRead(head, msg);
    return this;
}

// 一個靜態模板方法,掉用next的invokeChannelRead()方法
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
    final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeChannelRead(m);
    } else {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                next.invokeChannelRead(m);
            }
        });
    }
}

// 還是一個模板方法,用於真實執行hander的read事件處理方法
private void invokeChannelRead(Object msg) {
    if (invokeHandler()) {
        try {
          	// 觸發handler的read事件,模板模式
            ((ChannelInboundHandler) handler()).channelRead(this, msg);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    } else {
        fireChannelRead(msg);
    }
}

到這裏,事件的往後續handler傳遞,都是調用上面的這個兩個方法,來執行後續handler的相應read方法。此時pipeline中的handler有:

1) DefaultChannelPipeline$HeadContext

2) io.netty.handler.logging.LoggingHandler

3) io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor

4) DefaultChannelPipeline$TailContext

其中,head對read只是做簡單的傳遞:

// DefaultChannelPipeline$HeadContext
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ctx.fireChannelRead(msg);
}

// AbstractChannelHandlerContext.java
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
  	// 調用上面代碼片段中的靜態模板方法,實現事件傳遞
    invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
    return this;
}

LoggingHandler而言,簡易打印日誌,並往後傳遞事件:

// LoggingHandler.java
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (logger.isEnabled(internalLevel)) {
        logger.log(internalLevel, format(ctx, "READ", msg));
    }
    ctx.fireChannelRead(msg);
}

ServerBootstrapAcceptor的read方法時,初始化便真正地開始了(此時的線程爲bossGroup中的EventLoop):

// ServerBootstrapAcceptor
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;
		// 將ChannelInitializer添加到pipeline中,等執行完initial方法後,會被移除
    child.pipeline().addLast(childHandler);
		// 設置NioSocketChannel屬性
    setChannelOptions(child, childOptions, logger);
    setAttributes(child, childAttrs);
		
    try {
      	// 將NioSocketChannel綁定到一個workGroup中的NioEventLoop上
        childGroup.register(child).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    forceClose(child, future.cause());
                }
            }
        });
    } catch (Throwable t) {
        forceClose(child, t);
    }
}

register的過程與服務端啓動時的綁定類似,先選出一個EventLoop,選的時候,有兩種方式,根據不同的線程數,使用不同的選擇方式。然後經過輾轉,來到對register0()的執行,這個方法時主要的register操作。但是此時的線程是bossGroup中的EventLoop,而register0()會在workGroup中的線程中執行。所以會先將task放入隊列中,然後啓動線程,並進入NioEventLoop的run()死循環方法,通過不斷遍歷是否有已監聽事件以及執行隊列中的任務,最終來執行該task。

// MultithreadEventLoopGroup.java
@Override
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}

// SingleThreadEventLoop.java
@Override
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}

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

// AbstractChannel.java
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // ...
    AbstractChannel.this.eventLoop = eventLoop;
  	// 此時的線程是bossGroup中的EventLoop,
  	// 此處的eventLoop則爲上面分配的wordGroup中的線程。
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
      	// 所以會執行此方法,此時該eventLoop中的線程還未啓動,會將此task放入隊列中
      	// 然後會通過eventLoop.execute來啓動線程,並進入NioEventLoop的run()方法
      	// 通過不斷遍歷是否有已監聽事件以及執行隊列中的任務,最終來執行該task。
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        }
      	// ...
    }
}

此時,線程切換到workGroup中的EventLoop。主要執行好幾個操作:先調用jdk的api,註冊selectionKey;再發布相應的事件;最後修改interestOps爲OP_READ。

private void register0(ChannelPromise promise) {
    try {
        // ...
        boolean firstRegistration = neverRegistered;
      	// 調用jdk的api,註冊selectionKey
        doRegister();
        neverRegistered = false;
        registered = true;

        // 處理ChannelInitializer,並移除掉它
        pipeline.invokeHandlerAddedIfNeeded();
				// 在服務端啓動的時候,會以觀察者模式調用操作完成的Listener
      	// doBind操作就是這樣被封裝到了其中,但處理客戶端連接沒有doBind操作
        safeSetSuccess(promise);
      	// 從pipeline的head開始傳遞registered事件
        pipeline.fireChannelRegistered();
        // 此時已經被激活
        if (isActive()) {
          	// 第一次進行register操作,被視爲建立連接
            if (firstRegistration) {
              	// 與服務端類似,會在此處將監聽的事件改爲OP_READ
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                // 真的有數據來了
                beginRead();
            }
        }
    }
  	// ...
}

調用jdk的api,註冊selectionKey

// AbstractNioChannel.java
@Override
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
          	// 監聽的事件爲0,attachment是channel自己
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        }
      	// ...
    }
}

對channle已激活的事件傳遞中,會將NioSocketChannel的interestOps修改爲OP_READ。下面的代碼是事件在pipeline中的傳遞,與上面的分析內容一致,在此不多贅述。

// DefaultChannelPipeline.java
@Override
public final ChannelPipeline fireChannelActive() {
    AbstractChannelHandlerContext.invokeChannelActive(head);
    return this;
}

// AbstractChannelHandlerContext.java
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeChannelActive();
    } else {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                next.invokeChannelActive();
            }
        });
    }
}

private void invokeChannelActive() {
    if (invokeHandler()) {
        try {
            ((ChannelInboundHandler) handler()).channelActive(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    } else {
        fireChannelActive();
    }
}

唯一有區別的是:pipeline中的head,即HeadContext對active事件的處理方式,多了一塊對interestOps的處理:

// DefaultChannelPipeline.java $ HeadContext
@Override
public void channelActive(ChannelHandlerContext ctx) {
    ctx.fireChannelActive();
  	// 修改interestOps
    readIfIsAutoRead();
}

private void readIfIsAutoRead() {
    if (channel.config().isAutoRead()) {
        channel.read();
    }
}

這裏會在pipeline中傳遞read事件,但是是從tail開始,可以直接跳到TailContext的read()方法中:

// AbstractChannel.java
@Override
public Channel read() {
    pipeline.read();
    return this;
}

// DefaultChannelPipeline.java
@Override
public final ChannelPipeline read() {
    tail.read();
    return this;
}

// AbstractChannelHandlerContext.java
@Override
public ChannelHandlerContext read() {
    final AbstractChannelHandlerContext next = findContextOutbound(MASK_READ);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeRead();
    } else {
        Tasks tasks = next.invokeTasks;
        if (tasks == null) {
            next.invokeTasks = tasks = new Tasks(next);
        }
        executor.execute(tasks.invokeReadTask);
    }

    return this;
}

private void invokeRead() {
    if (invokeHandler()) {
        try {
            ((ChannelOutboundHandler) handler()).read(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    } else {
        read();
    }
}

// DefaultChannelPipeline.java
@Override
public void read(ChannelHandlerContext ctx) {
    unsafe.beginRead();
}

此處便到了修改的interestOps的主要邏輯處:

// AbstractChannel.java
@Override
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.java
@Override
protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;

    final int interestOps = selectionKey.interestOps();
  	// 此時interestOps爲0,所以if中的條件一定會成立
    if ((interestOps & readInterestOp) == 0) {
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

至此,對新建連接的處理基本完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章