netty服务端新连接接入的流程分析

由前面的分析可知NioEventLoop的run()方法是一个无限循环,NioEventLoop会不断的调用Selector的select(timeout)方法查询是否有新的IO事件,所以当一个客户端连接进入的时候会被Boss线程select到,故新连接接入流程的入口为Bose线程的select方法。

select(boolean oldWakenUp)方法如下所示(仅保留相关代码):

private void select(boolean oldWakenUp) throws IOException {
 	......
    int selectedKeys = selector.select(timeoutMillis);
	......
    if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
        break;
    }   
    ......
   
}

当客户端发起连接请求时,int selectedKeys = selector.select(timeoutMillis);返回的selectedKeys的值将会从0变为1。从而满足后面selectedKeys != 0 的判断,进而跳出select()方法,执行后续的操作。

select到IO事件后会进入NIOEventLoop的processSelectedKeys()方法,因为默认情况netty会对选择器Selector的SelectionKeys做相关优化(将原先的HashSet通过反射的方式修改为数组),故会执行NIOEventLoop的processSelectedKeysOptimized方法。

NIOEventLoop的processSelectedKeysOptimized()如下所示,因为当前是服务端,所以当前ServerSocketChannel是AbstractNioChannel类的子类,故进而由调用processSelectedKey(SelectionKey k, AbstractNioChannel ch)方法处理

private void processSelectedKeysOptimized() {
    for (int i = 0; i < selectedKeys.size; ++i) {
        final SelectionKey k = selectedKeys.keys[i];
        selectedKeys.keys[i] = null;

        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);
        }

        if (needsToSelectAgain) {
            selectedKeys.reset(i + 1);

            selectAgain();
            i = -1;
        }
    }
}

NIOEventLoop的processSelectedKey(SelectionKey k, AbstractNioChannel ch)方法如下所示

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
    final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
    if (!k.isValid()) {
        final EventLoop eventLoop;
        try {
            eventLoop = ch.eventLoop();
        } catch (Throwable ignored) {
            return;
        }
        if (eventLoop != this || eventLoop == null) {
            return;
        }
        unsafe.close(unsafe.voidPromise());
        return;
    }

    try {
        int readyOps = k.readyOps();
        if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
            int ops = k.interestOps();
            ops &= ~SelectionKey.OP_CONNECT;
            k.interestOps(ops);

            unsafe.finishConnect();
        }

        if ((readyOps & SelectionKey.OP_WRITE) != 0) {
            ch.unsafe().forceFlush();
        }

        if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
            unsafe.read();
        }
    } catch (CancelledKeyException ignored) {
        unsafe.close(unsafe.voidPromise());
    }
}

该方法的主要作用是根据SelectionKey中就绪事件的类型,执行不同的逻辑。因为当前分析的是服务端处理新连接的过程,由下面的代码片段可知,对于SelectionKey.OP_ACCEPT类型的事件,将会调用Unsafe接口的read()方法进行处理,这里实际调用的是AbstractNioMessageChannel的read()方法。

if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
     unsafe.read();
}

AbstractNioMessageChannel的read()方法如下:

read()方法中有三个核心的步骤,即:

int localRead = doReadMessages(readBuf);

**pipeline.fireChannelRead(readBuf.get(i));**

**pipeline.fireChannelReadComplete();**	
public void read() {
    assert eventLoop().inEventLoop();//断言当前线程为ServerSocketChannel绑定的EventLoop
    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 {
                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;
            pipeline.fireChannelRead(readBuf.get(i));//触发通道读时间
        }
        readBuf.clear();
        allocHandle.readComplete();
        pipeline.fireChannelReadComplete();//触发通道读取完成时间

        if (exception != null) {
            closed = closeOnReadError(exception);

            pipeline.fireExceptionCaught(exception);
        }

        if (closed) {
            inputShutdown = true;
            if (isOpen()) {
                close(voidPromise());
            }
        }
    } finally {
        if (!readPending && !config.isAutoRead()) {
            removeReadOp();
        }
    }
}

doReadMessages(List buf)

@Override
protected int doReadMessages(List<Object> buf) throws Exception {
    SocketChannel ch = SocketUtils.accept(javaChannel());//调用JDK的accpt方法获取一个客户端连接通道
    try {
        if (ch != null) {
            buf.add(new NioSocketChannel(this, ch));//创建一个netty的客户端通道类,并加入到集合中
            return 1;
        }
    } catch (Throwable t) {
        logger.warn("Failed to create a new channel from an accepted socket.", t);

        try {
            ch.close();
        } catch (Throwable t2) {
            logger.warn("Failed to close a socket.", t2);
        }
    }

    return 0;
}

fireChannelRead(Object msg)方法

该方法会触发Pipeline上所有的ChannelHandler的channelRead方法,最终会触发ServerBootstrapAcceptor类的channelRead方法,该实例是在ServerSocketChannel实例化的时候添加到pipeline的尾部的(ServerBootStrap的init(Channel channel)方法)

@Override
public final ChannelPipeline fireChannelRead(Object msg) {
    AbstractChannelHandlerContext.invokeChannelRead(head, msg);
    return this;
}

接着分析ServerBootstrapAcceptor的channelRead(ChannelHandlerContext ctx, Object msg)方法

该方法主要逻辑为:

1、根据ServerBootStrap中的配置,对客户端通道SocketChannel进行设置

2、在work线程组中选择一个NioEventLoop,并将当前客户连接注册在NioEventLoop的Selector上

当客户端后续再有read事件被触发,则会由当前work线程组中获得的NioEventLoop进行处理

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;//客户端连接channel

    child.pipeline().addLast(childHandler);//添加childHandler

    setChannelOptions(child, childOptions, logger);//设置通道的option
	//设置socketChannel的属性
    for (Entry<AttributeKey<?>, Object> e: childAttrs) {
        child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
    }
	//在work线程组里挑选一个EventLoop,并将当前的SocketChannel注册到EventLoop的Selector上
    try {
        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);
    }
}

pipeline.fireChannelReadComplete();

pipeline.fireChannelReadComplete();方法同上面fireChannelRead方法的流程类似,将Pipeline中的所有ChannelHandler中的channelReadComplete(ChannelHandlerContext ctx)方法都执行一遍

你可能感兴趣的:(源码学习)