我们再来讨论一下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.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
5.4 Acceptor Event Loop线程池
大家可能会发现VertxImpl类中还有一个acceptorEventLoopGroup。顾名思义,它是Netty中的Acceptor线程池,负责处理客户端的连接请求:
acceptorEventLoopGroup = new NioEventLoopGroup(1, acceptorEventLoopThreadFactory);
acceptorEventLoopGroup.setIoRatio(100);
由于系统只有一个服务端端口需要监听,因此这里只需要一个线程。
Vert.x中的HttpServer就利用了acceptorEventLoopGroup处理客户端的连接请求,具体的实现后边会另起一篇介绍。