Netty源码阅读3——服务端启动

Posted by 皮皮潘 on 12-03,2021

服务端的启动入口是在ServerBootstrap#bind()方法上,其他只是通过链式方法设置了对应的参数,在bind方法中先对于设置的参数进行校验,然后调用doBind()方法,触发真正的启动

private ChannelFuture doBind(final SocketAddress localAddress) {
    //...
    final ChannelFuture regFuture = initAndRegister();
    //...
    final Channel channel = regFuture.channel();
    //...
    doBind0(regFuture, channel, localAddress, promise);
    //...
    return promise;
}

其中核心在于initAndRegister方法以及doBind0方法

核心一:initAndRegister

final ChannelFuture initAndRegister() {
    // ...
    Channel channel = channelFactory.newChannel();
    //...
    init(channel);
    //...
    ChannelFuture regFuture = config().group().register(channel);
    //...
    return regFuture;
}

其中核心在于:1. 创建ServerChannel实例 2. 初始化对应的ServerChannel 3. 将ServerChannel注册到EventLoopGroup中去

  1. 创建ServerChannel实例:基于ReflectiveChannelFactory通过反射创建对应的实例,并调用了NioServerSocketChannel的实例化方法,在该方法中将会创建ServerSocketChannel(NIO中原生的Channel)并配置blocking为false、NioServerSocketChennelConfig、ChannelId、NioMessageUnsafe(实现具体的连接与读写数据,命名为Unsafe表示不对外提供并非不安全)以及DefaultChannelPipeline

  2. 初始化ServerChannel:配置了ServerChannel的Options以及Attributes,同时通过ChannelInitializer将ChannelHandler绑定到Pipeline中,同时在pipeline的末尾加入ServerBootstrapAcceptor去接收新连接

  3. 将ServerChannel注册到EventLoopGroup中去:底层调用了unsafe.register(),其中先将EventLoop事件循环器绑定到该NioServerSocketChannel上,然后通过eventLoop.execute异步调用 register0(),在register0中首先将Channel注册到Selector中去,此时只是简单地注册并没有指定感兴趣事件,然后激活invokerHandlerAddedIfNeeded最后激活fireChannelRegistered,但是ChannelActive事件并不是在register方法中激活的,而是在doBind0中激活的,因为Channel是否Active取决与是否isBound

核心二:doBind0

首先通过eventLoop().execute去异步调用channel.bind,channel.bind最后会调用Pipeline中的tail.bind方法并从尾部开始自内向外一路调用OutboundHandler.bind方法最后到达head.bind方法,并在head.bind方法中调用unsafe.bind,通过doBind真正完成ServerChannel和ip以及端口的绑定并激活ChannelActive事件,在激活ChannelActive事件之后会调用Pipeline中的tail.read方法并且再次从尾部开始自内向外一路调用OutboundHandler.read方法最后到达head.read方法,并在head.read方法中调用unsafe.beginRead,其中会将原来注册的未指定感兴趣事件(0)的SelectionKey指定为对于Accept事件感兴趣

源码分析小Tip

源码分析小Tip:对于异步执行的代码,可以在执行的地方打断点,然后通过调用栈找到是在哪里提交了对应的任务的并在提交的地方再打断点,然后重新启动应用并重复上述过程就可以对于异步执行的代码进行Debug