美文网首页
netty源码分析(二) - 服务端启动 - 2

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

作者: 进击的蚂蚁zzzliu | 来源:发表于2020-10-07 23:04 被阅读0次

    一、创建服务端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)
    • ChannelFuture regFuture = config().group().register(channel) 其中config().group()-NioEventLoopGroup;
    • next().register(channel)其中next()-NioEventLoop(NioEventLoop后面会详细讲解);
    • promise.channel().unsafe().register(this, promise)其中promise.channel().unsafe()-NioMessageUnsafe;
    1. this.eventLoop = eventLoop 绑定线程
    2. javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
    • 通过javaChannel()(上一《一、创建服务端Channel》中newSocket()创建的jdk的channel)调用jdk底层注册,把当前jdk的channel注册到selector(在NioEventLoop里后面会详细讲解)上面;
    • this:是netty的NioServerSocketChannel,作为attachment注册上去,方便后续从连接中获取
    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()

    相关文章

      网友评论

          本文标题:netty源码分析(二) - 服务端启动 - 2

          本文链接:https://www.haomeiwen.com/subject/yostpktx.html