Netty中的各个关键时间点(一)

前置说明

本文主要记录Netty中的一些主要的关键时间点,是理解Netty 和 事件驱动的关键。也是阅读Netty源码的指导。

代码来源:Netty 4.1.77

本文涉及到的角色:

角色:主线程,主Reactor线程(bossGroup中的EventLoop), 从Reactor线程(workGroup中的EventLoop),结合Reactor编码模型理解

PS:Netty中的主要事件节点不断完善中,看看后面要不要拆成多篇。。。

主Reactor线程的启动时间点

主线程调用ServerBootStrap.bind方法,会通过io.netty.bootstrap.AbstractBootstrap#initAndRegister方法,先初始化NioServerSocktChannel,然后把NioServerSocktChannel注册到主Reactor线程组中的一个主Reactor线程上

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            // 创建和初始化
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly();
            }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        //注册
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        return regFuture;
    }

最终会调用该主Reactor线程的注册方法进行注册:io.netty.channel.AbstractChannel.AbstractUnsafe#register,

        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            ...
            //检查相关代码忽略

            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    //当前是Main线程调用,所以提交任务到ServerChannel对应的Reactor线程
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    异常处理忽略。。。
                }
            }
        }

由于是主线程调用的该方法,所以会通过io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable) -> io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable, boolean) 提交一个异步任务到该主Reactor线程,

private void execute(Runnable task, boolean immediate) {
        boolean inEventLoop = inEventLoop();
        addTask(task); //把注册任务提交到主Reactor线程的任务队列
        if (!inEventLoop) {//当前是Main线程,需要启动主Reactor线程
            startThread();
            if (isShutdown()) {
                boolean reject = false;
                try {
                    if (removeTask(task)) {
                        reject = true;
                    }
                } catch (UnsupportedOperationException e) {

                }
                if (reject) {
                    reject();
                }
            }
        }

        if (!addTaskWakesUp && immediate) {
            wakeup(inEventLoop);
        }
    }

然后主线程调用io.netty.util.concurrent.SingleThreadEventExecutor#doStartThread方法启动主Reactor线程;

    private void doStartThread() {
        assert this.thread == null;

        this.executor.execute(new Runnable() {
            public void run() {
                SingleThreadEventExecutor.this.thread = Thread.currentThread();
                ...
                try {
                    ...
                    SingleThreadEventExecutor.this.run();
                    ...

在该io.netty.util.concurrent.SingleThreadEventExecutor#doStartThread方法中,是通过io.netty.util.concurrent.ThreadPerTaskExecutor#execute方法来提交任务,启动一个线程的,

public final class ThreadPerTaskExecutor implements Executor {
    private final ThreadFactory threadFactory;

    ...
    public void execute(Runnable command) {
        this.threadFactory.newThread(command).start();
    }
}

并且会把该线程赋值到主Reactor线程的io.netty.util.concurrent.SingleThreadEventExecutor#thread属性中,其实实际上的主Reactor线程应该是io.netty.util.concurrent.ThreadPerTaskExecutor#execute方法启动的线程;

此时,主Reactor线程就开始执行io.netty.channel.nio.NioEventLoop#run方法,进行主Reactor的核心逻辑了。

NioServerSocketChannel注册到主Reactor线程:

在上面的【主Reactor线程的启动时间点】流程中,我们知道,SeverBootStrap.bind方法会提交一个注册任务到主Reactor线程,主Reactor线程启动后,就会处理这个注册任务,该任务的核心代码为:io.netty.channel.AbstractChannel.AbstractUnsafe#register0 -> io.netty.channel.nio.AbstractNioChannel#doRegister

    @Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                //调用JDK中ServerSocketChannel的注册方法,把当前ServerSocketChannel注册到主Reactor线程中的selector上;
                //同时把Netty中的NioServerSocketChannel作为attachment,后面在通过JDK selector.select()获取事件的时候,可以获取到NioServerSocketChannel对象,方便执行Netty的操作
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                ...异常处理忽略
            }
        }
    }

这里最终调用的是JDK中ServerSocketChannel的注册方法,把当前ServerSocketChannel注册到主Reactor线程中的selector上;

注意这里java.nio.channels.SelectableChannel#register(java.nio.channels.Selector, int, java.lang.Object)的参数:

att:这里把Netty中的NioServerSocketChannel作为attachment,后面在通过JDK selector.select()获取事件的时候,可以获取到NioServerSocketChannel对象,方便执行Netty的操作。

ops:0, 这里注册ServerSocketChannel的时候,感兴趣的操作是0,而OP_ACCETP对应的值是16,

java.nio.channels.SelectionKey

public static final int OP_ACCEPT = 1 << 4; 

Netty这里并没有注册感兴趣的事件。OP_ACCETP事件的添加另有他处。

NioServerSocketChannel注册OP_ACCEPT

在ServerBootStrap.bind方法,首先会通过io.netty.bootstrap.AbstractBootstrap#initAndRegister方法,先初始化NioServerSocktChannel,然后把NioServerSocktChannel注册到主Reactor线程组中的一个主Reactor线程上的selector上,此时没有关注IO事件,因为java.nio.channels.SelectableChannel#register注册的ops参数为0,不代表任何IO事件。

NioServerSocketChannel异常注册的时候,bind方法还提交了一个bind任务到主Reactor线程。

    private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister(); //创建、初始化、异步注册NioServerSocketChannel
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            // 给异步注册任务添加Listener,在异步注册完成后,执行该Listener,通过doBind0向主Reactor线程提交bind任务
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        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;
        }
    }

    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

给异步注册任务添加Listener,在异步注册完成后,执行该Listener,向主Reactor线程提交bind任务。

该bind任务,最终会调用io.netty.channel.AbstractChannelHandlerContext#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise),在NioServerSocketChannel的pipeline中传播bind事件,该事件是一个outbound事件,会从tail开始,从后往前查找覆盖了bind方法的AbstractChannelHandlerContext,执行对应ChannelHandler的bind方法,最终会执行到HeadContext的bind方法:io.netty.channel.DefaultChannelPipeline.HeadContext#bind

        @Override
        public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
            unsafe.bind(localAddress, promise);
        }

这里我们重点关注bind的整体执行主体行为:

通过unsafe.bind执行底层JDK的bind操作;然后向主Reactor线程提交ChannelActive事件,在pipeline中传播ChannelActive事件

 @Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            检查代码忽略
            boolean wasActive = isActive(); //此时wasActive为false
            try {
                doBind(localAddress); //调用底层JDK方法bind
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }

            if (!wasActive && isActive()) {//doBind完成后,isActive为true
                invokeLater(new Runnable() {
                    @Override
                    public void run() {//向主Reactor线程提交ChannelActive事件
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

    @Override
    public boolean isActive() {
        // As java.nio.ServerSocketChannel.isBound() will continue to return true even after the channel was closed
        // we will also need to check if it is open.
        return isOpen() && javaChannel().socket().isBound();
    }

pipeline中ChannelActive事件的传播:

ChannelActive事件是inBound事件,会从当前ChannelHandlerContext向后查找覆盖了channelActive的ChannelHandler,进行处理

主要处理逻辑在HeadContext中,

        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            //pipeline中继续向后传播channelActive事件
            ctx.fireChannelActive();
            //如果是autoRead 则自动触发read事件传播
            //在read回调函数中 触发OP_ACCEPT注册
            readIfIsAutoRead();
        }

        @Override
        public Channel read() {
            pipeline.read();
            return this;
        }

        @Override
        public void read(ChannelHandlerContext ctx) {
            unsafe.beginRead();
        }

然后通过unsafe.beginRead注册OP_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;

        final int interestOps = selectionKey.interestOps();
        //这里readInterestOp就是NIOServerSocketChannel创建的时候传入的OP_ACCEPT,super(null, channel, SelectionKey.OP_ACCEPT);
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

此时,才完成了对OP_ACCEPT IO事件的关注监听。

主Reactor线程获取客户端连接时间:

当有客户端完成了三次握手后,服务端Socket会收到OP_ACCEPT事件,在主Reactor的核心逻辑io.netty.channel.nio.NioEventLoop#run方法中,会处理该OP_ACCEPT事件。

io.netty.channel.nio.NioEventLoop#processSelectedKeys -> io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        //获取Channel的底层操作类Unsafe
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            ......如果SelectionKey已经失效则关闭对应的Channel......
        }

        try {
            //获取IO就绪事件
            int readyOps = k.readyOps();
            //处理Connect事件
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                //移除对Connect事件的监听,否则Selector会一直通知
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
                //触发channelActive事件处理Connect事件
                unsafe.finishConnect();
            }

            //处理Write事件
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                ch.unsafe().forceFlush();
            }

             //处理Read事件或者Accept事件
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

然后会调用io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read方法进行底层操作,调用io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages方法,然后通过io.netty.util.internal.SocketUtils#accept方法,调用JDK中的java.nio.channels.ServerSocketChannel#accept获取客户端SocketChannel,并放入io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#readBuf容器中存放

然后遍历readBuf容器中的NioSocketChannel,通过NioServerSocketChannel#pipeline传播ChannelRead事件,最后会在io.netty.bootstrap.ServerBootstrap.ServerBootstrapAcceptor#channelRead的方法中处理,向从Reactor线程组注册NioSocketChannel,触发从Reactor线程的启动。(流程和主Reactor线程启动调用)

从Reactor线程启动时间:

在上面【主Reactor获取客户端连接】的时候,会向从Reactor线程注册,这里会提交任务触发从Reactor线程的启动。

NioSocketChannel添加关注事件:OP_READ

在【主Reactor线程获取客户端连接时间】流程中,我们知道NioServerSocketChannel向主Reactor线程注册的时候,会在pipeline中传播ChannelRead事件,ServerBootstrap.ServerBootstrapAcceptor#channelRead的方法会处理ChannelRead,向从Reactor线程组注册NioSocketChannel,触发从Reactor线程的启动

        @Override
        @SuppressWarnings("unchecked")
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
           ...
            try {
                // 选择一个从Reactor提交注册任务
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        ...
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

    //io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.ChannelPromise)
    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

    // io.netty.channel.AbstractChannel.AbstractUnsafe#register

这里的注册处理顺序与【NioServerSocketChannel注册OP_ACCEPT】顺序一致,且在调用JDK的时候,感兴趣的时间也是0;然后开始传播pipeline.fireChannelActive();

io.netty.channel.DefaultChannelPipeline.HeadContext#channelActive处理ChannelActive事件,

        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            ctx.fireChannelActive();

            readIfIsAutoRead();
        }

然后,io.netty.channel.Channel.Unsafe#beginRead -> io.netty.channel.nio.AbstractNioChannel#doBeginRead 

    @Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;

        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            // readInterestOp 在NioServerSocketChannel和NIOSocketChannel中不一样
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

这里与【NioServerSocketChannel注册OP_ACCEPT】的差异在于readInterestOp的值,这里是NIOSocketChannel,super(parent, ch, SelectionKey.OP_READ);

至此,OP_READ事件就可以监听了

从Reactor线程读取客户端数据时间

从Reactor线程向取客户端写数据时间

PS:耗时良久啊。相当于又看了几遍源码

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值