netty源码分析(二) - 服务端启动 - 2

2020-10-07  本文已影响0人  进击的蚂蚁zzzliu

一、创建服务端Channel

netty创建服务端channel.png
  1. newSocket():通过jdk创建底层jdk channel,SelectorProvider.provider()根据当前操作系统选择对应的provider;
  2. AbstractChannel:创建id/unsafe/pipeline
  3. AbstractNioChannel:configureBlocking(false),阻塞模式
  4. NioServerSocketChannelConfig:跟tcp相关参数的配置类,对创建出的channel参数进行配置;此处初始化时创建AdaptiveRecvByteBufAllocator(自适应内存分配器)并放到成员遍历allocator上

二、初始化服务端Channel

void init(Channel channel) {
    //配置用户自定义的options,对应启动时的.option()
    setChannelOptions(channel, newOptionsArray(), logger);
    //配置用户自定义的attributes,对应启动时的.attr()
    setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));

    ChannelPipeline p = channel.pipeline();

    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;
    //配置用户自定义的childOptions,对应启动时的.childOption(), 每次accept一个新连接都会配置到新创建的channel上
    synchronized (childOptions) {
        currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
    }
    //配置用户自定义的childAttributes,对应启动时的.childAttr(), 每次accept一个新连接都会配置到新创建的channel上
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);

    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(final Channel ch) {
            final ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = config.handler();
            if (handler != null) {
                //配置服务端pipeline, 对应启动时的.handler()
                pipeline.addLast(handler);
            }
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    //默认服务端pipeline都会添加一个ServerBootstrapAcceptor,这个特殊的channelHandler主要是用来给新创建的连接分配线程
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}
  1. 配置用户自定义的options,对应启动时的.option()
  2. 配置用户自定义的attributes,对应启动时的.attr()
  3. 配置用户自定义的childOptions,对应启动时的.childOption(), 每次accept一个新连接都会配置到新创建的channel上
  4. 配置用户自定义的childAttributes,对应启动时的.childAttr(), 每次accept一个新连接都会配置到新创建的channel上
  5. 配置服务端pipeline, 对应启动时的.handler()
  6. 默认服务端pipeline都会添加一个ServerBootstrapAcceptor,这个特殊的channelHandler主要是用来给新创建的连接分配线程

三、注册selector

注册selector.png
  1. 从代码上看先是把channel注册在group上,再从group里面找到loop把channel注册到loop上,最后变成把loop注册到channel上(通过unsafe)
  1. this.eventLoop = eventLoop 绑定线程
  2. javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
  1. pipeline.invokeHandlerAddedIfNeeded();事件的回调,在添加channelHandler到channel上时触发;对应handler中handlerAdded方法
  2. pipeline.fireChannelRegistered(); channel注册成功的事件传播出去;对应handler中channelRegistered方法

四、绑定端口

绑定端口.png
  1. regFuture.addListener添加operationComplete事件,异步执行内部doBind0方法进行端口绑定;
  2. doBind0中execute的Runnable实际上被当作task添加到taskQueue中; 在NioEventLoop run方法ranTasks = runAllTasks(0);时执行(这部分逻辑会在下一章节NioEventLoop中分析)
  3. channel.bind会通过channel中的pipeline进行绑定(pipeline.bind);pipeline上最终会在默认的HeadContext上通过unsafe调用到jdk底层bind方法进行绑定
  4. 绑定之后通过pipeline.fireChannelActive()把绑定完成事件传播出去
  5. 传播到HeadContext时会执行channel.read
  6. read方法通过pipeline执行,最终在默认的HeadContext上通过unsafe调用到jdk底层interestOps方法,注册感兴趣的事件
protected void doBeginRead() throws Exception {
    //《三、注册selector》时注册selector时返回的selectKey
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }
    readPending = true;
    //获取selectKey感兴趣的事件(当时注册的0)
    final int interestOps = selectionKey.interestOps();
    //readInterestOp是NioServerSocketChannel构造方法里设置的
    if ((interestOps & readInterestOp) == 0) {
        //在原interestOps基础上新增一个readInterestOp事件(OP_ACCEPT事件(16))
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}
至此,服务端启动流程就结束了

newChannel() ~> init() ~> register() ~> doBind()

上一篇下一篇

猜你喜欢

热点阅读