Netty-7 服务端和客户端创建流程

通过对源码的解读,会对Netty有跟深入的理解,从本篇开始,记录一些源码的理解,
本篇对入门案例的一些源码进行查看。

一、服务端流程

(一)服务端时序图

在这里插入图片描述

(二)步骤详解

  1. 创建ServerBootStrap实例。ServerBootStrap是Netty服务端启动的辅助类,提供了一些列的方法来设置服务端启动的相关参数。在构造的时候,我们并没有使用带参数的构造器,是因为参数太多,所以使用了Builder模式。
  2. 设置并绑定Reactor线程池。Netty中的EventLoopGroup实际上就是Reactor线程池,就是一个EventLoop数组。EventLoop用来处理注册到本线程的多路复用器Selector上的Channel,Selector的轮训操作由绑定的EventLoop线程run方法驱动,在一个循环体内循环执行。
  3. 设置并绑定服务端Channel。服务端需要创建NioServerSocketChannel,这里传入了一个class,使用工厂方法通过反射来完成创建。
    io.netty.bootstrap.AbstractBootstrap#channel:
  public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
      throw new NullPointerException("channelClass");
    } else {
      return this.channelFactory((io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
    }
  }
  1. 链路建立的时候,创建了ChannelPipeline,然后通过方法回到让用户来进行设置。ChannelPipeline采用了责任链模式,负责管理和执行ChannleHandler。
  2. 用户添加ChannelHandler。这里我们可以添加多个自定义的Handler以及系统预置的Handler。
  3. 绑定并启动监听端口。
  4. Selector轮询。由Reactor线程NioEventLoop负责调度和执行Selector轮询操作,选择准备就绪的Channel集合。
private void select(boolean oldWakenUp) throws IOException {
    Selector selector = this.selector;

    try {
      int selectCnt = 0;
      long currentTimeNanos = System.nanoTime();
      long selectDeadLineNanos = currentTimeNanos + this.delayNanos(currentTimeNanos);

      while(true) {
        long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
        if (timeoutMillis <= 0L) {
          if (selectCnt == 0) {
            selector.selectNow();
            selectCnt = 1;
          }
          break;
        }

        if (this.hasTasks() && this.wakenUp.compareAndSet(false, true)) {
          selector.selectNow();
          selectCnt = 1;
          break;
        }

        int selectedKeys = selector.select(timeoutMillis);
        ++selectCnt;
        if (selectedKeys != 0 || oldWakenUp || this.wakenUp.get() || this.hasTasks() || this.hasScheduledTasks()) {
          break;
        }

......

  1. 当轮询到准备就绪的Channel之后,就由Reactor线程NioEventLoop执行ChannelPipeline的响应方法,并最终调度ChannelHandler。
    在这里插入图片描述
  2. 执行ChannelHandler
  public ChannelHandlerContext fireChannelRead(Object msg) {
    invokeChannelRead(this.findContextInbound(), msg);
    return this;
  }

  static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
    final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
      next.invokeChannelRead(m);
    } else {
      executor.execute(new Runnable() {
        public void run() {
          next.invokeChannelRead(m);
        }
      });
    }

  }
  private AbstractChannelHandlerContext findContextInbound() {
    AbstractChannelHandlerContext ctx = this;

    do {
      ctx = ctx.next;
    } while(!ctx.inbound);

    return ctx;
  }
  可以看到,这里是把所有的ChannelHandler都执行了。

二、客户端流程

(一)时序图

在这里插入图片描述

(二)步骤详解

  1. 用户创建BootStap实例,设置客户端参数,异步发起客户端连接;
  2. 创建处理客户端连接、IO读写的Reactor线程组NioEventLoopGroup;
  3. 通过BootStrap的ChannelFactory和用户指定的Channel类型来创建用于客户端连接的NioSocketChannel,功能类似于NIO中的SocketChannel。
  4. 创建默认的ChannelPipeline,用于调度和执行网络事件;
  5. 异步发起TCP连接请求,成功就将SocketChannel注册到多路复用Selector上,监听读操作;
  6. 注册对应的网络监听位到多路复用器上;
  7. 在多路复用器上轮询各个Channel,处理连接结果;
  8. 如果连接成功,设置Future结果,发送连接成功事件,触发ChannelPipeline执行;
  9. 由ChannelPipeline调度执行系统和用户的ChannelHandler,执行业务逻辑;

猜你喜欢

转载自blog.csdn.net/mail_liuxing/article/details/90749535