Vertx tcpserver启动再次探秘

·  阅读 404

Vertx tcpserver启动再次探秘

我们之前已经弄明白了是如何启动一个TCP服务器了,现在要弄明白我们挂载的connectHandler和netsocket.handler是如何处理连接事件和读事件的

还记得在上篇文章写的是如何找到vertx和netty的交界点吗?我们是找到了一个listen方法内部出现了netty的Future

其在NetServerImpl中

public synchronized Future<NetServer> listen(SocketAddress localAddress) {
    //省略。。。
 io.netty.util.concurrent.Future<Channel> bindFuture = listen(localAddress, listenContext, new NetServerWorker(listenContext, handler, exceptionHandler));
//省略。。。
  }
复制代码

即我们使用connecthandler()函数绑定的链接处理器包裹在了这个NetServerWoker

请大家记住这个NetServerWorker后面还会遇到,此时我们再深入一层,追踪到我们上次追踪到的ServerBootStrap附近

在上面代码里面的listen方法的签名为public synchronized io.netty.util.concurrent.Future<Channel> listen(SocketAddress localAddress, ContextInternal context, Handler<Channel> worker),即传入的NetServerWorker当成了最后一个参数传入

再向下追踪到一个channelBalancer

以没有mainserver的情况举例子,其余情况同理

channelBalancer = new ServerChannelLoadBalancer(vertx.getAcceptorEventLoopGroup().next());
//此时包含我们连接处理器的NetServerWorker就放到balancer中了
//这个worker就是上面那个NetServerWorker
channelBalancer.addWorker(eventLoop, worker);
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(vertx.getAcceptorEventLoopGroup(),channelBalancer.workers());
bootstrap.childHandler(channelBalancer);

复制代码

此时我们切换到ServerChnannelLoadBalancer类,值得一提的是,他是Netty的ChannelInitializer的子类,所以其作为BootStrap最重要的函数就是initChannel(Channel)

protected void initChannel(Channel ch) {
    Handler<Channel> handler = chooseInitializer(ch.eventLoop());
    if (handler == null) {
      ch.close();
    } else {
        //用我们传入的NetServerWorker处理channel
        //这个handler从下面展示的两个方法源码可知就是我们传入的NetServerWorker
      channelGroup.add(ch);
      handler.handle(ch);
    }
  }
//根据此时的工作线程获取到handler,简化一下就是把NetServerWorker拿到了
private Handler<Channel> chooseInitializer(EventLoop worker) {
    WorkerList handlers = workerMap.get(worker);
    return handlers == null ? null : handlers.chooseHandler();
  }
//只要知道我们那个很重要的类放到了workerMap中handlers就可以了
public synchronized void addWorker(EventLoop eventLoop, Handler<Channel> handler) {
    workers.addWorker(eventLoop);
    WorkerList handlers = new WorkerList();
    WorkerList prev = workerMap.putIfAbsent(eventLoop, handlers);
    if (prev != null) {
      handlers = prev;
    }
    handlers.addWorker(handler);
    hasHandlers = true;
  }

复制代码

总结一下,本质上就是在ChannelInitializer里面用NetServerWorker使用handle(Channel)这个方法处理Netty中的Channel,现在可以把注意力转向这个重要的伏笔了

然后记住其内部的一个字段,这个就是我们对netserver实例挂载的连接处理器

private final Handler<NetSocket> connectionHandler;
复制代码

我们直接跳转到这个类的handle()

@Override
    public void handle(Channel ch) {
      if (!accept()) {
        ch.close();
        return;
      }
      if (HAProxyMessageCompletionHandler.canUseProxyProtocol(options.isUseProxyProtocol())) {
       //高可用配置。。忽略
           if (future.isSuccess()) {
            if (idle != null) {
              ch.pipeline().remove(idle);
            }
              //关注这个方法
            configurePipeline(future.getNow());
          } else {
            handleException(future.cause());
          }
      } else {
        configurePipeline(ch);
      }
    }
复制代码

在所有的分支语句里面都存在一个相同的方法调用,那么重点就在configurePipeline中了

private void configurePipeline(Channel ch) {
      if (sslHelper.isSSL()) {
        if (options.isSni()) {
          SniHandler sniHandler = new SniHandler(sslHelper.serverNameMapper(vertx));
            //挂载sni
          ch.pipeline().addLast("ssl", sniHandler);
        } else {
          SslHandler sslHandler = new SslHandler(sslHelper.createEngine(vertx));
          sslHandler.setHandshakeTimeout(sslHelper.getSslHandshakeTimeout(), sslHelper.getSslHandshakeTimeoutUnit());
            //挂载ssl
          ch.pipeline().addLast("ssl", sslHandler);
        }
        ChannelPromise p = ch.newPromise();
        ch.pipeline().addLast("handshaker", new SslHandshakeCompletionHandler(p));
        p.addListener(future -> {
          if (future.isSuccess()) {
              //重点
            connected(ch);
          } else {
            handleException(future.cause());
          }
        });
      } else {
        connected(ch);
      }
    }
复制代码

观察源码,我们可知最关键的函数是connect(Channel)

private void connected(Channel ch) {
      NetServerImpl.this.initChannel(ch.pipeline());
      //省略
      VertxHandler<NetSocketImpl> nh = VertxHandler.create(ctx -> new NetSocketImpl(context, ctx, sslHelper, metrics));
      nh.addHandler(conn -> {
        //省略...
        context.emit(conn, connectionHandler::handle);
      });
     //省略
      ch.pipeline().addLast("handler", nh);
    }
  }
复制代码

再次观察为了弄清楚到底在pipeline放了哪些handler,就要找pipeline去哪里了

第一处即第一行,很简单就是各种根据NetServerOptions加入需要的handler

protected void initChannel(ChannelPipeline pipeline) {
    if (options.getLogActivity()) {
      pipeline.addLast("logging", new LoggingHandler());
    }
    if (sslHelper.isSSL()) {
      // only add ChunkedWriteHandler when SSL is enabled otherwise it is not needed as FileRegion is used.
      pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());       // For large file / sendfile support
    }
    if (options.getIdleTimeout() > 0) {
      pipeline.addLast("idle", new IdleStateHandler(0, 0, options.getIdleTimeout(), options.getIdleTimeoutUnit()));
    }
  }
复制代码

第二处就是最后一行的把nh放到pipeline里面,而且我们connectHandler就是在这里面调用的,也就说关键在于VertxHandler里面,直接追到里面的addHandler()方法

而且通过检测VertxHandler的继承关系,其也是Netty channelhandler的子类

//此时泛型C的类型为NetSocketImpl
public VertxHandler<C> addHandler(Handler<C> handler) {
    this.addHandler = handler;
    return this;
  }
//反查addHandler的使用
private void setConnection(C connection) {
    
  //很重要
    conn = connection;
    if (addHandler != null) {
      addHandler.handle(connection);
    }
  }
//再反查上面方法的调用
//这个方法是Netty中的ChannelHandler中当此handler被加入时的回调
@Override
  public void handlerAdded(ChannelHandlerContext ctx) {
    setConnection(connectionFactory.apply(ctx));
  }
//再贴上构造函数和工程函数
public static <C extends ConnectionBase> VertxHandler<C> create(Function<ChannelHandlerContext, C> connectionFactory) {
    return new VertxHandler<>(connectionFactory);
  }
private VertxHandler(Function<ChannelHandlerContext, C> connectionFactory) {
    this.connectionFactory = connectionFactory;
  }


复制代码

到这里就明白了,通过在放入pipeline之前,给VertxHandler初始化addHandler字段,当我们把它放到pipeline的时候,netsocketImpl就生成了,并且赋值到内部字段conn上面了,然后通过其内部的addHandler处理这个NetSocketImpl,使用我们之前挂载的connectionHandler处理这个NetSocketImp

从时间序列上来讲其顺序为

handlerAdded->connectionFactory(用于生成NetsocketImp)->setConnection(用于把生成的初始化conn字段)

综上其实就是在做处理读事件之前的初始化

那么到底读的时候是如何调用到我们给netSocket挂载的Handler的呢?

这之前挂载到pipeline上面的并没有用于读取msg的handler,所以肯定是VertxHandler做了读事件的处理

所以直接查看作为channelhandler的核心方法channelRead

 public void channelRead(ChannelHandlerContext chctx, Object msg) {
    conn.read(msg);
  }
复制代码

这个conn是什么?就是上面我们初始化的netsocketImpl

其read方法最终调用的是NetSocketImpl内的HandlerMessage

再有pending字段实际处理msg

 public void handleMessage(Object msg) {
    if (msg instanceof ByteBuf) {
      msg = VertxHandler.safeBuffer((ByteBuf) msg);
    }
    context.emit(msg, elt -> {
      if (!pending.write(elt)) {
        doPause();
      }
    });
  }
复制代码

出现了一个没见过的东西pending这个就是一个字段,在构造器里面初始化的

pending = new InboundBuffer<>(context);
    pending.drainHandler(v -> doResume());
    pending.exceptionHandler(context::reportException);
//即我们netsocket.handler(handler)挂载的处理器又被封装了一层到这里面
    pending.handler(obj -> {
      if (obj == InboundBuffer.END_SENTINEL) {
        Handler<Void> handler = endHandler();
        if (handler != null) {
          handler.handle(null);
        }
      } else {
          //这个就是我们netsocket.handler(handler)挂载的处理器
        Handler<Object> handler = messageHandler();
        if (handler != null) {
          handler.handle(obj);
        }
      }
    });

 @Override
  public synchronized NetSocket handler(Handler<Buffer> dataHandler) {
    if (dataHandler != null) {
      messageHandler(new DataMessageHandler(dataHandler));
    } else {
      messageHandler(null);
    }
    return this;
  }
private synchronized Handler<Object> messageHandler() {
    return messageHandler;
  }
@Override
  public synchronized NetSocketInternal messageHandler(Handler<Object> handler) {
    messageHandler = handler;
    return this;
  }
复制代码

这下我们可以看看这个pending.write()是什么了,就是直接调用了我们挂载到netsocket上面的handler

public boolean write(E element) {
    checkThread();
    Handler<E> handler;
    synchronized (this) {
      if (demand == 0L || emitting) {
        pending.add(element);
        return checkWritable();
      } else {
        if (demand != Long.MAX_VALUE) {
          --demand;
        }
        emitting = true;
        handler = this.handler;
      }
    }
    handleEvent(handler, element);
    return emitPending();
  }
 private <T> void handleEvent(Handler<T> handler, T element) {
    if (handler != null) {
      try {
        handler.handle(element);
      } catch (Throwable t) {
        handleException(t);
      }
    }
  }
复制代码

总结一下

一个buffer从netty层面传递给VertxHandler中的channelRead方法,再转发到netsocket中,其再找到内部的pending,这里面封装了我们传入的netsocket.handler的处理器,再由pending调用这个处理器完成我们设定的函数

分类:
后端
标签: