Netty源码分析-启动流程

Netty源码分析-启动流程

原生NIO启动流程

// netty 中使用 NioEventLoopGroup (简称 nio boss 线程)来封装线程和 selector
Selector selector = Selector.open(); 

// 创建 NioServerSocketChannel,同时会初始化它关联的 handler,以及为原生 ssc 存储 config
NioServerSocketChannel attachment = new NioServerSocketChannel();

// 创建 NioServerSocketChannel 时,创建了 java 原生的 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
serverSocketChannel.configureBlocking(false);

// 启动 nio boss 线程执行接下来的操作

//注册(仅关联 selector 和 NioServerSocketChannel),未关注事件
SelectionKey selectionKey = serverSocketChannel.register(selector, 0, attachment);

// head -> 初始化器 -> ServerBootstrapAcceptor -> tail,初始化器是一次性的,只为添加 acceptor

// 绑定端口
serverSocketChannel.bind(new InetSocketAddress(8080));

// 触发 channel active 事件,在 head 中关注 op_accept 事件
selectionKey.interestOps(SelectionKey.OP_ACCEPT);
  1. 获得选择器Selector,Netty中使用NioEventloopGroup中的NioEventloop封装了线程和选择器
  2. 创建NioServerSocketChannel,该Channel作为附件添加到ServerSocketChannel中
  3. 创建ServerSocketChannel,将其设置为非阻塞模式,并注册到Selector中,此时未关注事件,但是添加了附件NioServerSocketChannel
  4. 绑定端口
  5. 通过interestOps设置感兴趣的事件

Netty启动流程

给出简易的Netty服务端的启动代码:

new ServerBootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        nioSocketChannel.pipeline().addLast(new LoggingHandler());
                    }
                }).bind(8080);

bind

选择器Selector的创建是在NioEventloopGroup中完成的。NioServerSocketChannelServerSocketChannel的创建,ServerSocketChannel注册到Selector中以及绑定操作都是由bind方法完成的。所以服务器启动的入口便是io.netty.bootstrap.ServerBootstrap.bind

public ChannelFuture bind(SocketAddress localAddress) {
    validate();
    return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}

真正完成初始化、注册以及绑定的方法是

重要源码:

private ChannelFuture doBind(final SocketAddress localAddress) {
    // 负责NioServerSocketChannel和ServerSocketChannel的创建
    // ServerSocketChannel的注册工作
    // init由main线程完成,regisetr由NIO线程完成
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }

    // 因为register操作是异步的
    // 所以要判断主线程执行到这里时,register操作是否已经执行完毕
    if (regFuture.isDone()) {
        // At this point we know that the registration was complete and successful.
        ChannelPromise promise = channel.newPromise();
        
        // 执行doBind0绑定操作
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
        // Registration future is almost always fulfilled already, but just in case it's not.
        // 如果register操作还没执行完,就会到这个分支中来
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        
        // 添加监听器,NIO线程异步进行doBind0操作
        regFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                Throwable cause = future.cause();
                if (cause != null) {
                    // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                    // IllegalStateException once we try to access the EventLoop of the Channel.
                    promise.setFailure(cause);
                } else {
                    // Registration was successful, so set the correct executor to use.
                    // See https://github.com/netty/netty/issues/2586
                    promise.registered();

                    doBind0(regFuture, channel, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

其中有两个重要的方法:initAndRegister()doBind0(regFuture,channel,localAddress,promise)

  • initAndRegister主要负责NioServerSocketChannel和ServerSocketChannel的创建(主线程中完成)与ServerSocketChannel注册(NIO线程中完成)工作

    init就对应原生NIO中的 : ServerSocketChannel ssc = ServerSocketChannel.open();

    register就对应原生NIO中的: SelectionKey selectionKey = ssc.register(selector,0,nettySsc);

  • doBind0则负责连接的创建工作,对应原生NIO中的 :ssc.bind(new InetSocketAddress(8080,backlog));

线程执行:

init 中 创建NioServerSocketChannel --- main 线程

--->添加NioServerSocketChannel 初始化 handler --- main 线程

register 中 启动 nio boss 线程 --- main 线程

--->原生ssc注册至selector未关注事件 --- nio 线程

--->执行NioServerSocketChannel 初始化 handler --- nio 线程

regFuture 等待回调doBind0 --- nio 线程

--->原生 ServerSocketChannel 绑定 --- nio 线程

--->触发NioServerSocketChannel active 事件 --- nio 线程

initAndRegister

final ChannelFuture initAndRegister() {
        Channel channel = null;
        // 此处执行init操作
        try {
            // 标识1: 通过工厂获取到创建的NioServerScoketChannel 
            channel = this.channelFactory.newChannel();
            // 标识2: 添加NioServerSocketChannel 初始化 handler
            this.init(channel);
        } catch (Throwable var3) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
            }

            return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
        }
        // 标识3:
        ChannelFuture regFuture = this.config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        return regFuture;
    }

标识1:

通过new Channel();创建channel,源码如下:

public T newChannel() {
        try {
          // 不难看出是通过反射进行channel的创建,在此处是获取到NioServerSocketChannel的构造
            return (Channel)this.constructor.newInstance();
        } catch (Throwable var2) {
            throw new ChannelException("Unable to create Channel from class " + this.constructor.getDeclaringClass(), var2);
        }
    }

NioServerSocketChannel的构造:

public NioServerSocketChannel() {
    // 创建了ServerSocketChannel实例
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

newSocket() :

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        // 此处相当于ServerSocketChannel.open()
      // 创建了ServerSocketChannel实例
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException("Failed to open a server socket.", e);
    }
}

原生的ServerSocketChannel中的open方法:

public static ServerSocketChannel open() throws IOException {
        return SelectorProvider.provider().openServerSocketChannel();
    }

可以看到,Netty中的NioServerSocketChannel与原生的ServerSocketChannel都调用了provider.openServerSocketChannel()来创建ServerSocketChannel实例。

至此,相当于完成了ServerSocketChannel ssc = ServerSocketChannel.open();


标识2:

init中重要源码部分:

@Override
void init(Channel channel) {
       ...
        
    // NioSocketChannl的Pipeline    
    ChannelPipeline p = channel.pipeline();
        
    ...

    // 向Pipeline中添加了一个handler,该handler等待被调用,ChannelInitializer 只会执行一次
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        // register之后才调用该方法,可以在此添加断点通过debug的方式查看何时被调用
        public void initChannel(final Channel ch) {
            final ChannelPipeline pipeline = ch.pipeline();
            
            // 创建handler并加入到pipeline中
            ChannelHandler handler = config.handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    // 添加新的handler,在发生Accept事件后建立连接                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

标识3:

该方法最终调用的是promise.channel().unsafe().register(this, promise)方法:

@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    ...

    // 获取EventLoop
    AbstractChannel.this.eventLoop = eventLoop;

       // 此处完成了由 主线程 到 NIO线程 的切换
    // eventLoop.inEventLoop()用于判断当前线程是不是eventLoop线程中的线程,也就是判断当前线程是否为NIO线程
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
      // 因为当前启动流程运行到这里是main线程,所以一定会进入到else中
        try {
            // 向eventLoop中的NIO线程添加任务,注意eventLoop为了节省资源,所以使用的是懒加载方式
            // 只有第一次执行execute方法时,才会去真正创建eventLoop关联的NIO线程
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    // 该方法中会执行doRegister
                    // 执行注册操作
                    register0(promise);
                }
            });
        } catch (Throwable t) {
           ...
        }
    }
}

register0( ):

private void register0(ChannelPromise promise) {
    try {
           ...
            
        // 执行真正的注册操作
        doRegister();
        neverRegistered = false;
        registered = true;

        // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
        // user may already fire events through the pipeline in the ChannelFutureListener.
        
        // 调用init中的initChannel方法
        pipeline.invokeHandlerAddedIfNeeded();
        // 向Promise中设置执行成功的结果,后续会通过promise的是否完成的值去选择执行doBind0()方法
        safeSetSuccess(promise);
        ...
    } catch (Throwable t) {
        ...
    }
}

后续ChannelFuture regFuture = initAndRegister();中的regFuture就是safeSetSuccess设置的promise,可以通过debug进行标识以及查看对象值来确定。

一般来说,真正执行操作的方法名前都会去加do,spring和Alibaba的源码中都是如此。

doRegister( ):

@Override
protected void doRegister() throws Exception {
    boolean selected = false;
    // 循环 CAS
    for (;;) {
        try {
            // javaChannel()即为Java中原生的ServerSocketChannel
            // eventLoop().unwrappedSelector()获取eventLoop中的Selector
            // this为NIOServerSocketChannel,作为附件
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
            ...
           
        }
    }
}

原生ssc注册至selector,此时并没用关注任何事件。此方法为NIO线程执行。

相当于执行了:SeletionKey selectionKey = ssc.register(selector,0,nettySsc);

返回至register0( )执行pipeline.invokeHandlerAddedIfNeeded();回调之前的init中的initChannel方法。


bind

执行绑定操作,重要源码部分:

public final void bind(SocketAddress localAddress, ChannelPromise promise) {
            this.assertEventLoop();
            if (promise.setUncancellable() && this.ensureOpen(promise)) {
                if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                    AbstractChannel.logger.warn("A non-root user can't receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
                }

                boolean wasActive = AbstractChannel.this.isActive();

                try {
                  // 真正执行 bind 绑定操作
                    AbstractChannel.this.doBind(localAddress);
                } catch (Throwable var5) {
                    this.safeSetFailure(promise, var5);
                    this.closeIfClosed();
                    return;
                }
                // 判断之前的ServerSocketChannel是否已经可用为active状态
                if (!wasActive && AbstractChannel.this.isActive()) {
                    this.invokeLater(new Runnable() {
                        public void run() {
                            // 触发这个ServerSocketChannel上的pipeline上的所有handler的active事件
                            AbstractChannel.this.pipeline.fireChannelActive();
                        }
                    });
                }

                this.safeSetSuccess(promise);
            }
        }

**doBind: **

@SuppressJava6Requirement(reason = "Usage guarded by java version check")
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
  // 判断java版本是不是大于7
    if (PlatformDependent.javaVersion() >= 7) {
        // 调用ServerSocketChannel的bind方法,绑定端口
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

javaChannel( )获取到的是ServerSocketChannel对象,使用原生的ServerSocketChannel进行绑定。

相当于执行了:ssc.bind(new InetSocketAddress(8080,backlog));


此时ServerSocketChannel 的 pipeline上拥有三个处理器:head->acceptor->tail。会先调用到headContext中的channelActive方法:

在一系列的调用过后,最终会完成相当于:selectionKey.interestOps(SelectionKey,OP_ACCEPT);的操作:
AbstractNioChannel.doBeginRead()方法中,会添加ServerSocketChannel添加Accept事件:

@Override
protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }
    readPending = true;
    // 获取Channel所有感兴趣的事件
    final int interestOps = selectionKey.interestOps();
    // 如果ServerSocketChannel没有关注Accept事件
    if ((interestOps & readInterestOp) == 0) {
        // 设置其感兴趣的事件,让其关注Accepet事件 
        // readInterestOp 取值是 16
        // 在 NioServerSocketChannel 创建时初始化
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

此时,获取channel关注感兴趣的事件的interestOps 因为没有关注事件所以是0,而readInterestOp是16,在SeletionKey中定义了:

public static final int OP_ACCEPT = 1 << 4; // 即值为16

interestOps | readInterestOp就相当于interestOps + readInterestOp 结果就是 16,也就使其关注了ACCEPT事件。

posted @ 2022-02-11 14:45  会编程的老六  阅读(54)  评论(0编辑  收藏  举报