第 10 章 Netty 核心源码剖析①

Netty 启动过程源码剖析
Netty 接受请求过程源码剖析

Pipeline Handler HandlerContext 创建源码剖析
ChannelPipeline 调度 handler 的源码剖析
Netty 心跳(heartbeat)服务源码剖析
Netty 核心组件 EventLoop 源码剖析
handler 中加入线程池和 Context 中添加线程池的源码剖析
在这里插入图片描述

//在 io.netty.example 包下,有很多Netty源码案例,可以用来分析

/** Server
 * Echoes back any received data from a client.
 */
public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server. 1111
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
        //2222
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     p.addLast(new LoggingHandler(LogLevel.INFO));
                     //p.addLast(new EchoServerHandler());
                 }
             });

            // Start the server. 3333
            ChannelFuture f = b.bind(PORT).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

10.1 Netty 启动过程源码剖析

在这里插入图片描述

说明:

1)先看启动类:main 方法中,首先创建了关于SSL 的配置类。

2)重点分析下 创建了两个EventLoopGroup 对象:

    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();

(1) 这两个对象是整个 Netty 的核心对象,可以说,整个 Netty 的运作都依赖于他们。bossGroup 用于接受 Tcp 请求,他会将请求交给 workerGroup ,workerGroup 会获取到真正的连接,然后和连接进行通信,比如读写解码编码等操作。

(2) EventLoopGroup 是 事件循环组(线程组) 含有多个 EventLoop, 可以注册channel ,用于在事件循环中去进行选择(和选择器相关).。
在这里插入图片描述

(3) new NioEventLoopGroup(1); 这个1 表示 bossGroup 事件组有1个线程你可以指定,如果 new NioEventLoopGroup() 会含有默认个线程 cpu核数*2, 即可以充分的利用多核的优势

 DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
 "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

会创建EventExecutor 数组 children = new EventExecutor[nThreads];

每个元素的类型就是 NIOEventLoop, NIOEventLoop 实现了 EventLoop 接口 和 Executor 接口
在这里插入图片描述
try 块中创建了一个 ServerBootstrap 对象,他是一个引导类,用于启动服务器和引导整个程序的初始化。它和 ServerChannel 关联, 而 ServerChannel 继承了 Channel,有一些方法 remoteAddress等。 随后,变量 b 调用了 group 方法将两个 group 放入了自己的字段中,用于后期引导使用
在这里插入图片描述
(4) 然后添加了一个 channel,其中参数一个Class对象,引导类将通过这个 Class 对象反射创建 ChannelFactory。然后添加了一些TCP的参数。[说明:Channel 的创建在bind 方法 ,会找到channel = channelFactory.newChannel(); ]
在这里插入图片描述
(5) 再添加了一个服务器专属的日志处理器 handler。
在这里插入图片描述
(6)再添加一个 SocketChannel(不是 ServerSocketChannel)的 handler。

(7)然后绑定端口并阻塞至连接成功。

(8)最后main线程阻塞等待关闭。

(9)finally 块中的代码将在服务器关闭时优雅关闭所有资源

分析 MultithreadEventExecutorGroup

参数说明:
@param nThreads         使用的线程数,默认为 core *2 [可以追踪源码]
@param executor         执行器:如果传入null,则采用Netty默认的线程工厂和默认的执行器ThreadPerTaskExecutor
@param chooserFactory   单例new DefaultEventExecutorChooserFactory()
@param args            args 在创建执行器的时候传入固定参数 
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
      EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) { //
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

        if (executor == null) { //如果传入的执行器是空的则采用默认的线程工厂和默认的执行器
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        //创建指定线程数的执行器数组
        children = new EventExecutor[nThreads];
        //初始化线程数组
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                // 创建 new NioEventLoop
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                // 如果创建失败,优雅关闭
                if (!success) {
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }

        chooser = chooserFactory.newChooser(children);

        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };

        //为每一个单例线程池添加一个关闭监听器
        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        //将所有的单例线程池添加到一个 HashSet 中。
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }
    
说明:
1)如果 executor 是null,创建一个默认的 ThreadPerTaskExecutor,使用 Netty 默认的线程工厂。
2)根据传入的线程数(CPU*2)创建一个线程池(单例线程池)数组。
3)循环填充数组中的元素。如果异常,则关闭所有的单例线程池。
4)根据线程选择工厂创建一个 线程选择器。
5)为每一个单例线程池添加一个关闭监听器。将所有的单例线程池添加到一个 HashSet 中。

ServerBootstrap 创建和构造过程

ServerBootstrap 是个空构造,但是有默认的成员变量
在这里插入图片描述
分析一下 ServerBootstrap 基本使用情况

ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(new EchoServerHandler());
                 }
             });
             
说明:
1)链式调用:group方法,将boss和worker传入,boss 赋值给parentGroup属性,worker 赋值给 childGroup 属性
2)channel 方法传入 NioServerSocketChannel class 对象。会根据这个 class 创建 channel 对象。
3)option 方法传入 TCP 参数,放在一个LinkedHashMap 中。
4)handler 方法传入一个 handler 中,这个hanlder 只专属于 ServerSocketChannel 而不是 SocketChannel
 childHandler 传入一个 hanlder ,这个handler 将会在每个客户端连接的时候调用。供 SocketChannel 使用

绑定端口的分析

// Start the server.
ChannelFuture f = b.bind(PORT).sync();

服务器就是在这个bind方法里启动完成的

bind 方法代码, 追踪到 创建了一个端口对象,并做了一些空判断, 核心代码doBind,我们看看
 public ChannelFuture bind(SocketAddress localAddress) {
                validate();
                if (localAddress == null) {
                    throw new NullPointerException("localAddress");
                }
                return doBind(localAddress);
            }

doBind 源码剖析, 核心是两个方法 initAndRegister 和  doBind0

private ChannelFuture doBind(final SocketAddress localAddress) {
            final ChannelFuture regFuture = initAndRegister();//下面分析
            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 方法,完成对端口的绑定
                //============================================
                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);
                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;
            }
    }

分析说明 final ChannelFuture regFuture = initAndRegister();


final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
//说明: channelFactory.newChannel() 方法 的作用 通过 ServerBootstrap 的通道工厂
        //反射创建一个 NioServerSocketChannel, 具体追踪源码可以得到下面结论
(1) 通过 NIO 的SelectorProvider 的 openServerSocketChannel 方法得到JDK 的 channel。
    目的是让 Netty 包装 JDK 的 channel。
(2) 创建了一个唯一的 ChannelId,创建了一个 NioMessageUnsafe,用于操作消息,
    创建了一个 DefaultChannelPipeline 管道,是个双向链表结构,用于过滤所有的进出的消息。
(3) 创建了一个 NioServerSocketChannelConfig 对象,用于对外展示一些配置。
            channel = channelFactory.newChannel();//NioServerSocketChannel
          
            
//说明:init 初始化这个 NioServerSocketChannel, 具体追踪源码可以得到如下结论
(1) init 方法,这是个抽象方法(AbstractBootstrap类的),由 ServerBootstrap 实现
    (可以追一下源码 //setChannelOptions(channel, options, logger);)。
(2) 设置 NioServerSocketChannel 的 TCP 属性。
(3) 由于 LinkedHashMap 是非线程安全的,使用同步进行处理。
(4) 对 NioServerSocketChannel 的 ChannelPipeline 添加 ChannelInitializer 处理器。
(5) 可以看出, init 的方法的核心作用在和 ChannelPipeline 相关。
(6) 从 NioServerSocketChannel 的初始化过程中,我们知道,pipeline 是一个双向链表,
    并且,他本身就初始化了 head 和 tail,这里调用了他的 addLast 方法,
    也就是将整个 handler 插入到 tail 的前面,因为 tail 永远会在后面,需要做一些系统的固定工作。
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            return new DefaultChannelPromise(new FailedChannel(), 
            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;
    }
    
说明:
1)基本说明: initAndRegister() 初始化 NioServerSocketChannel 通道并注册各个 handler,返回一个 future
2)通过 ServerBootstrap 的通道工厂反射创建一个 NioServerSocketChannel。
3)init 初始化这个 NioServerSocketChannel。
4)config().group().register(channel) 通过 ServerBootstrap 的 bossGroup 注册 NioServerSocketChannel。
5)最后,返回这个异步执行的占位符即 regFuture。

init(channel) 方法 会调用addLast, 现在进入到 addLast 方法内查看

 public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);
            //ChannelHandlerContext是添加Handler时创建的
            newCtx = newContext(group, filterName(name, handler), handler);
            addLast0(newCtx);
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }
            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        
        callHandlerAdded0(newCtx);
        return this;
    }
说明:
1)addLast方法,在 DefaultChannelPipeline 类中
2)addLast方法这就是 pipeline 方法的核心
3)检查该 handler 是否符合标准。
4)创建一个 AbstractChannelHandlerContext 对象,这里说一下,
ChannelHandlerContext 对象是 ChannelHandler 和 ChannelPipeline 之间的关联,
每当有 ChannelHandler 添加到 Pipeline 中时,都会创建 Context。
Context 的主要功能是管理他所关联的 Handler 和同一个 Pipeline 中的其他 Handler 之间的交互。
5)将 Context 添加到链表中。也就是追加到 tail 节点的前面。最后,同步或者异步或者晚点异步的调用 callHandlerAdded0 方法

 private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

分析: doBind0(regFuture, channel, localAddress, promise);
前面说了 dobind方法有2个重要的步骤,initAndRegister 说完,接下来看 doBind0 方法, 代码如下
在这里插入图片描述

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()) {
                   //bind方法这里下断点,这里下断点,来玩!!
                 channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }


说明:
1)该方法的参数为 initAndRegister 的 future,NioServerSocketChannel,端口地址,NioServerSocketChannel 的 promise

2)这里就可以根据前面下的断点,一直debug: 将调用 LoggingHandler 的 invokeBind 方法, 最后会追到
            //DefaultChannelPipeline 类的bind
            //然后进入到 unsafe.bind方法debug , 注意要追踪到
            // unsafe.bind , 要debug 第二圈的时候,才能看到.
                @Override
        public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
                throws Exception {
            unsafe.bind(localAddress, promise);
        }
            //继续追踪 AbstractChannel 的
            public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
                //....

            try {
                // 可以看到,这里最终的方法就是 doBind 方法,执行成功后,执行通道的 fireChannelActive 方法,
                //告诉所有的 handler,已经成功绑定。
                doBind(localAddress);//
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }
            }

最终doBind 就会追踪到 NioServerSocketChannel的doBind, 说明 Netty 底层使用的是 Nio
 @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }
 protected ServerSocketChannel javaChannel() {
        return (ServerSocketChannel) super.javaChannel();
    }
    
    
回到 bind 方法(alt+v),最后一步:safeSetSuccess(promise),告诉 promise 任务成功了。
其可以执行监听器的方法了。到此整个启动过程已经结束了 ,ok了


继续 atl+V服务器就回进入到(NioEventLoop类)一个循环代码,进行监听

  @Override
    protected void run() {
        for (;;) {
            try {
    }

在这里插入图片描述
在这里插入图片描述

10.2 Netty 接受请求过程源码剖析

在这里插入图片描述
在这里插入图片描述
分析: processSelectedKeys(); masterGroup怎么将请求Client的Channel注册到到workerGroup的的NioEventLoop的Selector上的???
在这里插入图片描述

1.断点位置 NioEventLoop 的如下方法 processSelectedKey

 if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read(); //断点位置
            }

2.执行 浏览器 http://localhost:8007/, 客户端发出请求

3.从的断点我们可以看到, readyOps 是16 ,也就是 Accept事件。说明浏览器的请求已经进来了。

4.这个 unsafe 是 boss 线程NioServerSocketChannelAbstractNioMessageChannel$NioMessageUnsafe 对象。 我们进入到AbstractNioMessageChannel$NioMessageUnsafe 的read 方法中

5.read 方法代码并分析:

@Override
        public void read() {
            assert eventLoop().inEventLoop();
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);

            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        int localRead = doReadMessages(readBuf);////111
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }

                        allocHandle.incMessagesRead(localRead);
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    pipeline.fireChannelRead(readBuf.get(i));////222
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (exception != null) {
                    closed = closeOnReadError(exception);

                    pipeline.fireExceptionCaught(exception);
                }

                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } finally {
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }
说明:
  1. 检查该 eventloop 线程是否是当前线程。assert eventLoop().inEventLoop()

  2. 执行 doReadMessages 方法,并传入一个 readBuf 变量,这个变量是一个 List,也就是容器。

  3. 循环容器,执行 pipeline.fireChannelRead(readBuf.get(i));

  4. doReadMessages 是读取 boss 线程中的 NioServerSocketChannel 接受到的请求。并把这些请求放进容器,一会我们debug 下doReadMessages 方法.

  5. 循环遍历 容器中的所有请求,调用 pipeline 的 fireChannelRead 方法,用于处理这些接受的请求或者其他事件,在read 方法中,循环调用 ServerSocket 的 pipeline 的 fireChannelRead 方法, 开始执行 管道中的 handler 的 ChannelRead 方法(debug 进入)

6.追踪一下doReadMessages方法, 就可以看得更清晰

protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = SocketUtils.accept(javaChannel());
        buf.add(new NioSocketChannel(this, ch));
        return 1;
    }

说明:

  1. 通过工具类,调用 NioServerSocketChannel 内部封装的 serverSocketChannel 的 accept 方法,这是 Nio 做法。
  2. 获取到一个 JDK 的 SocketChannel,然后,使用 NioSocketChannel 进行封装。最后添加到容器中
  3. 这样容器buf 中就有了NioSocketChannel

7.回到read方法,继续分析 循环执行 pipeline.fireChannelRead 方法

  1. 前面分析 doReadMessages 方法的作用是通过 ServerSocket 的 accept 方法获取到 Tcp 连接,然后封装成 Netty 的 NioSocketChannel 对象。最后添加到 容器中

  2. 在read 方法中,循环调用 ServerSocket 的 pipeline 的 fireChannelRead 方法, 开始执行 管道中的 handler 的 ChannelRead 方法(debug 进入)
    在这里插入图片描述

  3. 经过dubug (多次),可以看到会反复执行多个 handler的ChannelRead ,我们知道,因为pipeline 里面又 4 个 handler ,分别是 Head,LoggingHandler,ServerBootstrapAcceptor,Tail。
    在这里插入图片描述

  4. 我们重点看看 ServerBootstrapAcceptor。debug 之后,断点会进入到 ServerBootstrapAcceptor 中来。我们来看看 ServerBootstrapAcceptor 的 channelRead 方法(要多次debug才可以)

  5. channelRead 方法

io.netty.bootstrap.ServerBootstrap.ServerBootstrapAcceptor#channelRead

public void channelRead(ChannelHandlerContext ctx, Object msg) {
            final Channel child = (Channel) msg;

            child.pipeline().addLast(childHandler);

            setChannelOptions(child, childOptions, logger);

            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }

            try {//将客户端连接注册到 worker 线程池
            //将该 NioSocketChannel 注册到 childGroup 中的一个 EventLoop 上,并添加一个监听器。
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

说明:

  1. msg 强转成 Channel ,实际上就是 NioSocketChannel 。
  2. 添加 NioSocketChannel 的 pipeline 的 handler ,就是我们 main 方法里面设置的 childHandler 方法里的。
  3. 设置 NioSocketChannel 的各种属性。
  4. 将该 NioSocketChannel 注册到 childGroup 中的一个 EventLoop 上(默认轮询next方法选出),并添加一个监听器。
  5. 这个 childGroup 就是我们 main 方法创建的数组 workerGroup。

8.进入 register 方法查看(步步追踪会到)

@Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {

            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {

                eventLoop.execute(new Runnable() {
                    @Override
                    public void run() {
                        register0(promise);//进入到这里
                    }
                });

            }
        }

继续进入到 下面方法, 执行管道中可能存在的任务, 这里我们就不追了

9.最终会调用 doBeginRead 方法,也就是 AbstractNioChannel 类的方法

@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) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

10.这个地方调试时,请把前面的断点都去掉,然后启动服务器就会停止在 doBeginRead(需要先放过该断点,然后浏览器请求,才能看到效果)

11.执行到这里时,针对于这个客户端的连接就完成了,接下来就可以监听读事件了

在这里插入图片描述

register0  ------  beginRead ----- doBeginRead
在这里插入图片描述

发布了138 篇原创文章 · 获赞 3 · 访问量 7219

猜你喜欢

转载自blog.csdn.net/weixin_43719015/article/details/105331660