• Netty 源码(二)NioEventLoop 之 Channel 注册


    Netty 源码(二)NioEventLoop 之 Channel 注册

    Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)

    相关文章:

    Channel 注册

    1. Channel 注册到 NioEventLoop

    chnnel 初始化完成后就需要将其注册到对应的 NioEventLoop 上。

    (1) NioEventLoopGroup 注册

    // NioEventLoopGroup -> MultithreadEventLoopGroup
    public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }
    

    group() 返回的是上文配置的 childGroup 对象,即 NioEventLoopGroup,每个 NioEventLoopGroup 持有多个 NioEventLoop,next 依次返回一个 NioEventLoop。

    (2) NioEventLoop 注册

    // NioEventLoop -> SingleThreadEventLoop
    public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }
    

    可以看到最终调用对应 channel 的 unsafe 进行注册,下面看一下 unsafe 是如何注册的。

    (3) Unsafe 注册

    NioServerSocketChannel 的 unsafe = new NioMessageUnsafe(),而 NioMessageUnsafe 继承自 AbstractUnsafe。

    // NioServerSocketChannel -> AbstractChannel.AbstractUnsafe
    public final void register(EventLoop eventLoop, final ChannelPromise promise) {
        // 省略...
        AbstractChannel.this.eventLoop = eventLoop;
        // 同一个 channel 的注册、读、写等都在 eventLoop 完成,避免多线程的锁竞争
        if (eventLoop.inEventLoop()) {
            // 将 channel 注册到 eventLoop 上
            register0(promise);
        } else {
            try {
                eventLoop.execute(new Runnable() {
                    @Override
                    public void run() {
                        register0(promise);
                    }
                });
            } catch (Throwable t) {
                // 省略...
            }
        }
    }
    private void register0(ChannelPromise promise) {
        // 1. 确保 channel 的状态是 open,最终调用  ch.isOpen()
        if (!promise.setUncancellable() || !ensureOpen(promise)) {
            return;
        }
        boolean firstRegistration = neverRegistered;
        // 2. channel 注册到 eventLoop 上
        doRegister();
        neverRegistered = false;
        registered = true;
    
        // 3. 此时 channel 已经注册到 eventLoop 上,此时需要将注册的 handler 绑定到 channel 上。eg: ChannelInitializer
        pipeline.invokeHandlerAddedIfNeeded();
    
        safeSetSuccess(promise);
        pipeline.fireChannelRegistered();
        // 4. channel 状态为 active 就需要触发 ChannelActive 事件或准备读
        if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    }
    

    register 中最重要的方法是调用 doRegister 进行注册,该方法调用了 JDK 底层的代码进行注册。

    (4) doRegister

    // AbstractNioChannel
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                // 1. 调用 JDK NIO 将 channel 注册到 eventLoop 的 selector 上
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // 2. 调用 selectNow 将注册的 channel 移除,不然有可能被缓存没有删除
                    //    移除后继续注册
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // 3. JDK 提供的文档此时不会出现该异常,实际... JDK bug
                    throw e;
                }
            }
        }
    }
    

    2. 注册感兴趣的事件

    AbstractNioChannel#javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
    

    register 方法注册 Java 原生 NIO 的 Channel 对象到 Selector 对象。但是为什么感兴趣事件的是 0 呢?正常情况下,对于服务端来说,需要注册 SelectionKey.OP_ACCEPT 事件。

    (1) 注册方式是多态的,它即可以被 NIOServerSocketChannel 用来监听客户端的连接接入,也可以注册 SocketChannel 用来监听冉莹颖读或者写操作。

    (2) 通过 SelectionKey#interestOps(int ops) 方法可以方便地修改监听操作位。所以,此处注册需要获取 SelectionKey 并给 AbstractNIOChannel 的成员变量 selectionKey 赋值。

    • SelectionKey.OP_READ(1)
    • SelectionKey.OP_WRITE(4)
    • SelectionKey.OP_CONNECT(8)
    • SelectionKey.OP_ACCEPT(16)
    // AbstractChannel.AbstractUnsafe
    public final void beginRead() {
        assertEventLoop();
        if (!isActive()) {
            return;
        }
    
        try {
            doBeginRead();
        } catch (final Exception e) {
            invokeLater(new Runnable() {
                @Override
                public void run() {
                    pipeline.fireExceptionCaught(e);
                }
            });
            close(voidPromise());
        }
    }
    
    // AbstractNioChannel
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }
    
        readPending = true;
    
        // 重新注册感兴趣的事件类型,readInterestOp 是 channel 初始化的时候传进来的
        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }
    

    到此 NioServerSocketChannel 就重新注册上了 OP_ACCEPT 事件


    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    jupyter中使用熟悉的vim
    解决安装manjaro中安装ccs10.2的库缺失问题
    Markdown中公式
    诗就应该边读边品的,不要
    为neovim田间python3支持
    bilibili视频保存目录
    新工科教育--之我所见
    父母的爱 和汽车的后背箱
    解决manjaro中jupyter无法修改目录和默认浏览器的问题:
    解决jupyter的能打开python文件无法新建的问题
  • 原文地址:https://www.cnblogs.com/binarylei/p/10135712.html
Copyright © 2020-2023  润新知