Vert.x的线程的总结2

4.3 Verticle

我们再来讨论一下Verticle中的Context。在部署Verticle的时候,Vert.x会根据配置来创建Context并绑定到Verticle上,此后此Verticle上所有绑定的Handler都会在此Context上执行。相关实现位于doDeploy方法,这里摘取核心部分:

for (Verticle verticle: verticles) {
  WorkerExecutorImpl workerExec = poolName != null ? vertx.createSharedWorkerExecutor(poolName, options.getWorkerPoolSize()) : null;
  WorkerPool pool = workerExec != null ? workerExec.getPool() : null;
  // 根据配置创建Context
  ContextImpl context = options.isWorker() ? vertx.createWorkerContext(options.isMultiThreaded(), deploymentID, pool, conf, tccl) :
    vertx.createEventLoopContext(deploymentID, pool, conf, tccl);
  if (workerExec != null) {
    context.addCloseHook(workerExec);
  }
  context.setDeployment(deployment);
  deployment.addVerticle(new VerticleHolder(verticle, context));
  // 此Verticle上的Handler都会在创建的context作用域内执行
  context.runOnContext(v -> {
    try {
      verticle.init(vertx, context);
      Future<Void> startFuture = Future.future();
      // 大家熟悉的start方法的执行点
      verticle.start(startFuture);
      startFuture.setHandler(ar -> {
        if (ar.succeeded()) {
          if (parent != null) {
            parent.addChild(deployment);
            deployment.child = true;
          }
          vertx.metricsSPI().verticleDeployed(verticle);
          deployments.put(deploymentID, deployment);
          if (deployCount.incrementAndGet() == verticles.length) {
            reportSuccess(deploymentID, callingContext, completionHandler);
          }
        } else if (!failureReported.get()) {
          reportFailure(ar.cause(), callingContext, completionHandler);
        }
      });
    } catch (Throwable t) {
      reportFailure(t, callingContext, completionHandler);
    }
  });
}

通过这样一种方式,Vert.x保证了Verticle的线程安全 —— 即某个Verticle上的所有Handler都会在同一个Vert.x线程上执行,这样也保证了Verticle内部成员的安全(没有race condition问题)。比如下面Verticle中处理IO及事件的处理都一直是在同一个Vert.x线程下执行的,每次打印出的线程名称应该是一样的:

public class TcpClientVerticle extends AbstractVerticle {
  int i = 0;
  @Override
  public void start() throws Exception {
    vertx.createNetClient().connect(6666, "localhost", ar -> {
      if (ar.succeeded()) {
        NetSocket socket = ar.result();
        System.out.println(Thread.currentThread().getName());
        socket.handler(buffer -> {
          i++;
          System.out.println(Thread.currentThread().getName());
          System.out.println("Net client receiving: " + buffer.toString("UTF-8"));
        });
        socket.write("+1s\n");
      } else {
        ar.cause().printStackTrace();
      }
    });
  }
}

5.线程池

5.1 Event Loop线程池

之前我们已经提到过,Event Loop线程池的类型为Netty中的NioEventLoopGroup,里面的线程通过Vert.x自己的线程工厂VertxThreadFactory进行创建:

eventLoopThreadFactory = new VertxThreadFactory("vert.x-eventloop-thread-", checker, false, options.getMaxEventLoopExecuteTime());
eventLoopGroup = new NioEventLoopGroup(options.getEventLoopPoolSize(), eventLoopThreadFactory);
eventLoopGroup.setIoRatio(NETTY_IO_RATIO);

其中Event Loop线程的数目可以在配置中指定。

5.2 Worker线程池
在之前讲executeBlocking底层实现的文章中我们已经提到过Worker线程池,它其实就是一种Fixed Thread Pool:

ExecutorService workerExec = Executors.newFixedThreadPool(options.getWorkerPoolSize(),
    new VertxThreadFactory("vert.x-worker-thread-", checker, true, options.getMaxWorkerExecuteTime()));
PoolMetrics workerPoolMetrics = isMetricsEnabled() ? metrics.createMetrics(workerExec, "worker", "vert.x-worker-thread", options.getWorkerPoolSize()) : null;
workerPool = new WorkerPool(workerExec, workerPoolMetrics);

Worker线程同样由VertxThreadFactory构造,类型为VertxThread,用于执行阻塞任务。我们同样可以在配置中指定其数目。

5.3 内部阻塞线程池
ExecutorService internalBlockingExec =

Executors.newFixedThreadPool(options.getInternalBlockingPoolSize(),
    new VertxThreadFactory("vert.x-internal-blocking-", checker, true, options.getMaxWorkerExecuteTime()));
PoolMetrics internalBlockingPoolMetrics = isMetricsEnabled() ? metrics.createMetrics(internalBlockingExec, "worker", "vert.x-internal-blocking", options.getInternalBlockingPoolSize()) : null;
internalBlockingPool = new WorkerPool(internalBlockingExec, internalBlockingPoolMetrics);

Internal Blocking Pool可能设计用于内部使用,在executeBlocking(Action action, Handler resultHandler)这个版本的方法中就使用了它。

5.4 Acceptor Event Loop线程池
大家可能会发现VertxImpl类中还有一个acceptorEventLoopGroup。顾名思义,它是Netty中的Acceptor线程池,负责处理客户端的连接请求:

acceptorEventLoopGroup = new NioEventLoopGroup(1, acceptorEventLoopThreadFactory);
acceptorEventLoopGroup.setIoRatio(100);

由于系统只有一个服务端端口需要监听,因此这里只需要一个线程。
Vert.x中的HttpServer就利用了acceptorEventLoopGroup处理客户端的连接请求,具体的实现后边会另起一篇介绍。

你可能感兴趣的:(Vert.x)