Vert.x 中,多线程环境下的资源管理和状态维护是一个复杂的问题。为了解决这个问题,Vert.x 引入了 Context
这个核心概念。Context
负责在多线程环境下协调异步操作,提供线程安全的资源访问,并确保异步操作的正确执行顺序。本文将对 Vert.x 的 Context 进行源码解析,探讨它在异步编程中的作用、设计原理以及关键的实现细节。
此方法就是一个接口,通过实现该接口,做为回调使用
@FunctionalInterface
public interface Handler<E> {
/**
* Something has happened, so handle it.
*
* @param event the event to handle
*/
void handle(E event);
}
Context接口,在此方法内部定义了基本的事件处理,比如立即执行任务,阻塞线程执行任务等。
public interface Context {
/**
* 当前线程是否在work线程
*/
static boolean isOnWorkerThread() {
Thread t = Thread.currentThread();
return t instanceof VertxThread && ((VertxThread) t).isWorker();
}
/**
* 当前线程是否在eventloop线程
*/
static boolean isOnEventLoopThread() {
Thread t = Thread.currentThread();
return t instanceof VertxThread && !((VertxThread) t).isWorker();
}
/**
* 当前线程是否是vertx线程
*/
static boolean isOnVertxThread() {
return Thread.currentThread() instanceof VertxThread;
}
/**
* 当前线程立马运行handler任务,最后调用的也是dispatch
*/
void runOnContext(Handler<Void> action);
/**
* work线程上运行,运行耗时任务,默认如果超出十秒则会写入日志。最后调用的也是dispatch
* @param blockingCodeHandler 执行逻辑
* @param ordered 如果为true则进入队列,这样子会串行执行,如果为false则立马执行这样子是并发执行
* @param
* @return
*/
<T> Future<@Nullable T> executeBlocking(Handler<Promise<T>> blockingCodeHandler, boolean ordered);
/**
* 是否是eventloopContext
*/
boolean isEventLoopContext();
/**
* 是否是workContext
*/
boolean isWorkerContext();
//get put 是context内部的一个ConcurrentHashMap,用于存储值,其实还有localdatade的get,put方法
<T> T get(Object key);
void put(Object key, Object value);
//部署id
String deploymentID();
}
ContextInternal是Context接口的一个扩展,它增加了一些内部使用的方法和功能。它通常不直接暴露给应用程序开发者,比如内部获取当前线程的Context。
dispatch中beginDispatch和endDispatch是一个很重要的方法,如果是Vertx的线程他是让当前线程所绑定的Context先切换当下的Context,接着在end里面重新切换回去。如果不是Vertx的线程就用ThreadLocal进行切换当前Context,在end中切换回去
public interface ContextInternal extends Context {
//获取当前线程Context,所以原则上每个线程只有一个Context
static ContextInternal current() {
Thread thread = Thread.currentThread();
//如果是Vertx的线程,则直接获取,因为每个Vertx线程创建的时候会绑定Context
if (thread instanceof VertxThread) {
return ((VertxThread) thread).context();
} else {
//如果不是Vert的线程则从ThreadLocal里进行获取
VertxImpl.ContextDispatch current = VertxImpl.nonVertxContextDispatch.get();
if (current != null) {
return current.context;
}
}
return null;
}
/**
* 里面包含线程池,包括对线程池进行监测性能的PoolMetrics
* @return the context worker pool
*/
WorkerPool workerPool();
@Override
/**
* 通过执行器立马执行任务,参数就是Handler
* @param action the action to run
*/
@Override
default void runOnContext(Handler<Void> action) {
executor().execute(() -> dispatch(action));
}
default void dispatch(Handler<Void> handler) {
dispatch(null, handler);
}
//你看dispatch其实就是执行传入进来的Handler类的handle方法
//这个方法很重要的方法就是讲线程绑定的context做切换
default <E> void dispatch(E event, Handler<E> handler) {
//beginDispatch,endDispatch主要是调用Vertx的方法执行,
//context做切换以及比如一些执行时间统计、类加载器绑定等
ContextInternal prev = beginDispatch();
try {
//重点执行handle
handler.handle(event);
} catch (Throwable t) {
reportException(t);
} finally {
endDispatch(prev);
}
}
/**
* 切换当前的上下文Context
*/
default ContextInternal beginDispatch() {
VertxImpl vertx = (VertxImpl) owner();
return vertx.beginDispatch(this);
}
/**
* 重新切换原先的上下文Context
*/
default void endDispatch(ContextInternal previous) {
VertxImpl vertx = (VertxImpl) owner();
vertx.endDispatch(previous);
}
}
vertx.beginDispatch
该方法是Vertx的实现类VertxImpl里的。
具体就是先获取原先该线程或者ThreadLocal有无绑定的Context,有的话把这个context保留下来赋值给prev,并且把传入进来的context与当前线程和ThreadLocal绑定
//存储Context的类
static final ThreadLocal<ContextDispatch> nonVertxContextDispatch = new ThreadLocal<>();
//内部包含context以及classLoader
static class ContextDispatch {
ContextInternal context;
ClassLoader topLevelTCCL;
}
ContextInternal beginDispatch(ContextInternal context) {
//获取当前线程
Thread thread = Thread.currentThread();
ContextInternal prev;
//判断是否是VertxThread
if (thread instanceof VertxThread) {
//如果是则直接根据VertxThread里获取Context
VertxThread vertxThread = (VertxThread) thread;
prev = vertxThread.context;
//执行时间统计
if (!ContextBase.DISABLE_TIMINGS) {
vertxThread.executeStart();
}
//将线程context改成传入进来的context
vertxThread.context = context;
if (!disableTCCL) {
if (prev == null) {
vertxThread.topLevelTCCL = Thread.currentThread().getContextClassLoader();
}
if (context != null) {
thread.setContextClassLoader(context.classLoader());
}
}
} else {
//如果不是则执行该方法
prev = beginDispatch2(thread, context);
}
//最后返回原先的Context(这里保留原先的context主要是后面之行结束了,线程的context要切换回去)
return prev;
}
-----------------------beginDispatch2-------------------方法
private ContextInternal beginDispatch2(Thread thread, ContextInternal context) {
//从ThreadLocal里进行获取ContextDispatch,内部就是Context以及classload
ContextDispatch current = nonVertxContextDispatch.get();
ContextInternal prev;
//如果当前的不为空,则将prev设置成当前的context
if (current != null) {
prev = current.context;
} else {
//如果为空,则重新new一个设置会null并且在thradlocal里存储
current = new ContextDispatch();
nonVertxContextDispatch.set(current);
prev = null;
}
//将传入的context赋值给当前线程context
current.context = context;
if (!disableTCCL) {
if (prev == null) {
current.topLevelTCCL = Thread.currentThread().getContextClassLoader();
}
thread.setContextClassLoader(context.classLoader());
}
//返回原先(当前)的context
return prev;
}
vertx.endDispatch
这里就是将startDispatch返回的原始绑定的Context传入进来进行切换。
void endDispatch(ContextInternal prev) {
//获取当前线程,prev是刚刚startDispathc返回的值传进来的
Thread thread = Thread.currentThread();
if (thread instanceof VertxThread) {
VertxThread vertxThread = (VertxThread) thread;
//将当前线程的context切换回原来的
vertxThread.context = prev;
if (!disableTCCL) {
ClassLoader tccl;
if (prev == null) {
tccl = vertxThread.topLevelTCCL;
vertxThread.topLevelTCCL = null;
} else {
tccl = prev.classLoader();
}
Thread.currentThread().setContextClassLoader(tccl);
}
//这边就是结束时间
if (!ContextBase.DISABLE_TIMINGS) {
vertxThread.executeEnd();
}
} else {
endDispatch2(prev);
}
}
private void endDispatch2(ContextInternal prev) {
ClassLoader tccl;
//这边也是同理将原先的prev绑定重新给到threadLocal,如果为空则直接remove
ContextDispatch current = nonVertxContextDispatch.get();
if (prev != null) {
current.context = prev;
tccl = prev.classLoader();
} else {
nonVertxContextDispatch.remove();
tccl = current.topLevelTCCL;
}
if (!disableTCCL) {
Thread.currentThread().setContextClassLoader(tccl);
}
}
是ContextInternal的实现类,它包含具体功能的实现,比如执行耗时任务,
public abstract class ContextBase implements ContextInternal {
//数据存储
private ConcurrentMap<Object, Object> data;
//本地数据存储
private ConcurrentMap<Object, Object> localData;
//内部任务队列
final TaskQueue internalOrderedTasks;
//内部任务线程池
final WorkerPool internalWorkerPool;
//工作线程池,外部调用
final WorkerPool workerPool;
//任务队列
final TaskQueue orderedTasks;
//eventloop线程
private final EventLoop eventLoop;
........
/**
*
* @param vertx
* @param eventLoop 它从eventGroup里进行取出来的,用于处理事件
* @param internalWorkerPool 可能会造成阻塞的内部任务执行器
* @param workerPool 工作现场执行器,用于普通的异步任务
* @param deployment 用于管理当前Verticle的部署和卸载
* @param closeFuture 关闭的future
* @param tccl 当前线程的classLoader
*/
protected ContextBase(VertxInternal vertx,
EventLoop eventLoop,
WorkerPool internalWorkerPool,
WorkerPool workerPool,
Deployment deployment,
CloseFuture closeFuture,
ClassLoader tccl) {
this.deployment = deployment;
this.config = deployment != null ? deployment.config() : new JsonObject();
this.eventLoop = eventLoop;
this.tccl = tccl;
this.owner = vertx;
this.workerPool = workerPool;
this.closeFuture = closeFuture;
this.internalWorkerPool = internalWorkerPool;
//有序任务队列,内部都是用的LinkedList,目的肯定是有序
this.orderedTasks = new TaskQueue();
//用于阻塞的任务队列
this.internalOrderedTasks = new TaskQueue();
}
//-------------------executeBlocking系列--------------------------------------------
@Override
public <T> Future<T> executeBlockingInternal(Handler<Promise<T>> action) {
return executeBlocking(this, action, internalWorkerPool, internalOrderedTasks);
}
@Override
public <T> Future<T> executeBlockingInternal(Handler<Promise<T>> action, boolean ordered) {
return executeBlocking(this, action, internalWorkerPool, ordered ? internalOrderedTasks : null);
}
@Override
public <T> Future<T> executeBlocking(Handler<Promise<T>> blockingCodeHandler, boolean ordered) {
return executeBlocking(this, blockingCodeHandler, workerPool, ordered ? orderedTasks : null);
}
@Override
public <T> Future<T> executeBlocking(Handler<Promise<T>> blockingCodeHandler, TaskQueue queue) {
return executeBlocking(this, blockingCodeHandler, workerPool, queue);
}
/**
* 上面的方法最后还是调用该方法
* @param context contex上下文就是本身
* @param blockingCodeHandler 就是传入进来的Handler(实际需要执行的任务)
* @param workerPool 内部包含线程池以及性能检测指标类
* @param queue 队列,根据是否有队列来判断是顺序执行还是并发执行
* @param
* @return
*/
static <T> Future<T> executeBlocking(ContextInternal context, Handler<Promise<T>> blockingCodeHandler,
WorkerPool workerPool, TaskQueue queue) {
//是个接口,根据实现它的begin、rejected、end内写入监测代码,后文给你展示下
PoolMetrics metrics = workerPool.metrics();
Object queueMetric = metrics != null ? metrics.submitted() : null;
//获取Promise,就是我们前面ContextInternal的方法
Promise<T> promise = context.promise();
Future<T> fut = promise.future();
try {
//创建Runnable
Runnable command = () -> {
Object execMetric = null;
//这边就是当他不为空时会调用监测类的begin方法
if (metrics != null) {
execMetric = metrics.begin(queueMetric);
}
//这个方法就是ContextInternal内实现的方法,就是调用handle.handle,这里重新创建了一个Handler,并且把传入进来的Handler写在它的实现方法里
context.dispatch(promise, f -> {
try {
//传递了promise,promise是用来告知结果的
blockingCodeHandler.handle(promise);
} catch (Throwable e) {
promise.tryFail(e);
}
});
//这里也是监测类的end方法
if (metrics != null) {
metrics.end(execMetric, fut.succeeded());
}
};
//这里获取workerPool的线程池
Executor exec = workerPool.executor();
//如果队列没有则直接线程池执行,如果有则提交给队列执行
if (queue != null) {
queue.execute(command, exec);
} else {
exec.execute(command);
}
} catch (RejectedExecutionException e) {
// 任务异常监测调用
if (metrics != null) {
metrics.rejected(queueMetric);
}
throw e;
}
//这里返回future
return fut;
}
//-------------------execute,runOnContext,emit--------------------------------------------
//exceute以及runOnContext,emit实际执行方法都交由子类进行执行
@Override
public void execute(Runnable task) {
execute(this, task);
}
protected abstract <T> void execute(ContextInternal ctx, Runnable task);
@Override
public final <T> void execute(T argument, Handler<T> task) {
execute(this, argument, task);
}
protected abstract <T> void execute(ContextInternal ctx, T argument, Handler<T> task);
@Override
public <T> void emit(T argument, Handler<T> task) {
emit(this, argument, task);
}
protected abstract <T> void emit(ContextInternal ctx, T argument, Handler<T> task);
}
内部定义了一个LinkedList来存储任务,接着按照LinkedList添加顺序来顺序执行任务,因为代码量比较少,我全部添加进来并且加了注释。
public class TaskQueue {
static final Logger log = LoggerFactory.getLogger(TaskQueue.class);
private static class Task {
private final Runnable runnable;
private final Executor exec;
public Task(Runnable runnable, Executor exec) {
this.runnable = runnable;
this.exec = exec;
}
}
// @protectedby tasks
private final LinkedList<Task> tasks = new LinkedList<>();
// @protectedby tasks
private Executor current;
private final Runnable runner;
//在开始就进行初始化了runner执行的就是run方法
public TaskQueue() {
runner = this::run;
}
/**
* Run a task.
*
* @param task the task to run.
*/
public void execute(Runnable task, Executor executor) {
//同步,防止多线程添加,这样子能保证顺序
synchronized (tasks) {
//将任务添加进去
tasks.add(new Task(task, executor));
//判断当前线程池是否为空,为空则赋值,并且通过executor执行runner,实际执行的就是下面那个run方法
if (current == null) {
current = executor;
try {
executor.execute(runner);
} catch (RejectedExecutionException e) {
current = null;
throw e;
}
}
}
}
//实际执行的就是这一段,把task里的任务获取进行执行,没有任务则退出,不然就死循环执行
private void run() {
for (; ; ) {
final Task task;
//加锁,保证任务队列的操作是线程安全的
synchronized (tasks) {
task = tasks.poll();
//没有任务直接退出死循环
if (task == null) {
current = null;
return;
}
//如果task的线程与当前线程不是同一个线程
if (task.exec != current) {
//不是则将任务重新添加进tasks
tasks.addFirst(task);
//并且重新执行run方法
task.exec.execute(runner);
//设置当前current位当前的线程
current = task.exec;
return;
}
}
try {
//是当前线程直接执行
task.runnable.run();
} catch (Throwable t) {
log.error("Caught unexpected Throwable", t);
}
}
};
}
可以根据实现PoolMetrics在其begin、rejected、end内写入监测代码
public interface PoolMetrics<T> extends Metrics {
/**
* 任务提交
*/
default T submitted() {
return null;
}
/**
* 任务开始
* The submitted task start to use the resource.
*
*/
default T begin(T t) {
return null;
}
/**
* 记录任务被拒绝执行
*/
default void rejected(T t) {
}
/**
* 任务结束
*/
default void end(T t, boolean succeeded) {
}
}
public class WorkerContext extends ContextBase {
WorkerContext(VertxInternal vertx,
WorkerPool internalBlockingPool,
WorkerPool workerPool,
Deployment deployment,
CloseFuture closeFuture,
ClassLoader tccl) {
super(vertx, vertx.getEventLoopGroup().next(), internalBlockingPool, workerPool, deployment, closeFuture, tccl);
}
//----------------------runOnContext实际实现-----------------------------
@Override
protected void runOnContext(ContextInternal ctx, Handler<Void> action) {
try {
run(ctx, null, action);
} catch (RejectedExecutionException ignore) {
// Pool is already shut down
}
}
private <T> void run(ContextInternal ctx, T value, Handler<T> task) {
Objects.requireNonNull(task, "Task handler must not be null");
//调用executor(),以及newRunaable,内部就是ContextInternal的ctx.dispatch(value, task)方法
executor().execute(() -> ctx.dispatch(value, task));
}
/**
* 看代码实际上也是加入到orderedTasks队列进行执行
* @return
*/
@Override
public Executor executor() {
if (executor == null) {
executor = command -> {
PoolMetrics metrics = workerPool.metrics();
Object queueMetric = metrics != null ? metrics.submitted() : null;
//加入到队列
orderedTasks.execute(() -> {
Object execMetric = null;
if (metrics != null) {
execMetric = metrics.begin(queueMetric);
}
try {
command.run();
} finally {
if (metrics != null) {
metrics.end(execMetric, true);
}
}
}, workerPool.executor());
};
}
return executor;
}
//---------------------------execute和emit实际实现----------------------------
//execute和emit实现的都是调用execute(orderedTasks, argument, task)方法,无非就是一个通过外部Handler,一个内部又new Handler了一个把传入进来的 //Handler给了ContextInternal的ctx.dispatch(value, task)方法执行
@Override
protected <T> void execute(ContextInternal ctx, T argument, Handler<T> task) {
execute(orderedTasks, argument, task);
}
@Override
protected <T> void emit(ContextInternal ctx, T argument, Handler<T> task) {
execute(orderedTasks, argument, arg -> {
ctx.dispatch(arg, task);
});
}
/**
* 执行任务
* @param queue 队列
* @param argument 执行返回值
* @param task task handler
* @param
*/
private <T> void execute(TaskQueue queue, T argument, Handler<T> task) {
//判断当前调用线程是否work线程,是的话则直接执行,不是的话进入队列执行
if (Context.isOnWorkerThread()) {
task.handle(argument);
} else {
PoolMetrics metrics = workerPool.metrics();
Object queueMetric = metrics != null ? metrics.submitted() : null;
//进入队列执行
queue.execute(() -> {
Object execMetric = null;
if (metrics != null) {
execMetric = metrics.begin(queueMetric);
}
try {
task.handle(argument);
} finally {
if (metrics != null) {
metrics.end(execMetric, true);
}
}
}, workerPool.executor());
}
}
}
**execute(): **
1.如果当前线程是work线程则直接执行。
2.如果不是则进入orderedTasks队列顺序执行,并且在执行前面添加了PoolMetrics的监测方法。
3.但是都带有返回值
runOnContext:
进入orderedTasks队列顺序执行,用的workerpool,并且在执行前面添加了PoolMetrics的监测方法
emit:
1.如果当前线程是work线程则直接执行。
2.如果不是则进入orderedTasks队列顺序执行,并且在执行前面添加了PoolMetrics的监测方法。
3.但是都带有返回值
4.唯一与execute不同的是它重新创建了一个handler,把传入进来的handler给了ContextInternal.dispatch执行
public class EventLoopContext extends ContextBase {
//都是调用的父类,所以注释写在父类里面
EventLoopContext(VertxInternal vertx,
EventLoop eventLoop,
WorkerPool internalBlockingPool,
WorkerPool workerPool,
Deployment deployment,
CloseFuture closeFuture,
ClassLoader tccl) {
super(vertx, eventLoop, internalBlockingPool, workerPool, deployment, closeFuture, tccl);
}
//----------------------runOnContext实际实现-----------------------------
/**
* 直接EventLoop直接执行
* @param ctx
* @param action
*/
@Override
protected void runOnContext(ContextInternal ctx, Handler<Void> action) {
try {
nettyEventLoop().execute(() -> ctx.dispatch(action));
} catch (RejectedExecutionException ignore) {
// Pool is already shut down
}
}
public EventLoop nettyEventLoop() {
return eventLoop;
}
//---------------------------execute和emit实际实现----------------------------
/**
* 直接在eventloop里面进行执行
* @param ctx
* @param argument
* @param task
* @param
*/
@Override
protected <T> void emit(ContextInternal ctx, T argument, Handler<T> task) {
EventLoop eventLoop = nettyEventLoop();
//如果是eventloop直接执行
if (eventLoop.inEventLoop()) {
ContextInternal prev = ctx.beginDispatch();
try {
task.handle(argument);
} catch (Throwable t) {
reportException(t);
} finally {
ctx.endDispatch(prev);
}
} else {
//如果不是则直接调用eventloop线程执行
eventLoop.execute(() -> emit(ctx, argument, task));
}
}
/**
* 还是在eventloop内部执行
*
* - When the current thread is event-loop thread of this context the implementation will execute the {@code task} directly
* - Otherwise the task will be scheduled on the event-loop thread for execution
*
*/
@Override
protected <T> void execute(ContextInternal ctx, T argument, Handler<T> task) {
EventLoop eventLoop = nettyEventLoop();
if (eventLoop.inEventLoop()) {
task.handle(argument);
} else {
eventLoop.execute(() -> task.handle(argument));
}
}
/**
* 还是在eventloop内部执行
* @param ctx
* @param task
* @param
*/
@Override
protected <T> void execute(ContextInternal ctx, Runnable task) {
EventLoop eventLoop = nettyEventLoop();
if (eventLoop.inEventLoop()) {
task.run();
} else {
eventLoop.execute(task);
}
}
}
execute(): 进入EventLoop线程直接执行
runOnContext:直接也是进入EventLoop线程调用ContextInternal.dispatch执行
emit:使用EventLoop线程执行,自己加上了beginDispatch和end方法,实际是和ContextInternal的dispatch内部方法是一样的
主要总结:
ContextBase内部默认实现了executeBlocking和executeBlockingInternal方法,用这个来执行队列任务。
EventLoopContext则是全部由EventLoop线程来进行执行
WorkerContext则主要是通过线程池进队列执行。
那么它是在哪里使用的呢,它其实贯穿Vertx的始末,只要由任务执行,线程相关都有它。接下来我举例子。
首先它是在VertImpl里进行创建的
比如创建EventLoopContext的创建和WorkContext的创建
@Override
public EventLoopContext createEventLoopContext(Deployment deployment, CloseFuture closeFuture, WorkerPool workerPool, ClassLoader tccl) {
return new EventLoopContext(this, eventLoopGroup.next(), internalWorkerPool, workerPool != null ? workerPool : this.workerPool, deployment, closeFuture, disableTCCL ? null : tccl);
}
@Override
public EventLoopContext createEventLoopContext(EventLoop eventLoop, WorkerPool workerPool, ClassLoader tccl) {
return new EventLoopContext(this, eventLoop, internalWorkerPool, workerPool != null ? workerPool : this.workerPool, null, closeFuture, disableTCCL ? tccl : null);
}
@Override
public EventLoopContext createEventLoopContext() {
return createEventLoopContext(null, closeFuture, null, Thread.currentThread().getContextClassLoader());
}
@Override
public WorkerContext createWorkerContext(Deployment deployment, CloseFuture closeFuture, WorkerPool workerPool, ClassLoader tccl) {
return new WorkerContext(this, internalWorkerPool, workerPool != null ? workerPool : this.workerPool, deployment, closeFuture, disableTCCL ? null : tccl);
}
@Override
public WorkerContext createWorkerContext() {
return createWorkerContext(null, closeFuture, null, Thread.currentThread().getContextClassLoader());
}
那么在哪里调用呢
VertxImpl
先获取再调用
//比如先获取后创建
public ContextInternal getOrCreateContext() {
ContextInternal ctx = getContext();
if (ctx == null) {
// We are running embedded - Create a context
//如果为空则进行创建并且加入到stickyContext里面
ctx = createEventLoopContext();
stickyContext.set(new WeakReference<>(ctx));
}
return ctx;
}
DeploymentManager的doDeploy方法
就是再Vert.x调用deployVerticle进行部署的时候会调用
ContextBase context = (options.isWorker() ? vertx.createWorkerContext(deployment, closeFuture, workerPool, tccl) :
使用的地方太多了,包括NetServer,NetClient等等等等,它内部的EventLoop就是用于Netty的客户端服务端收发信息所用,后续将Netserve和NetClient等会详细说明
Context和线程其实没有强关联关系。
当部署的时候会创建一个新的Context。
当不是部署的时候获取Context会首先获取Thread以及ThreadLocal里的Context,如果没有的话则会创建一个EventLoopContext并且添加到ThreadLocal。但是这是会更改的,当当前的线程通过其他Context执行任务的时候就会将当前线程的Context进行替换,直到执行完任务才会将Context替换回去
问题
为什么要先介绍Context呢?
因为Vertx的Context贯穿始末,包括Server,部署,EventBus等等等等,都是会用到的,因为它负责管理线程,异步任务执行。
源码注释版本
我目前在源码上都加注释,后续如果有需要可以找我拿加了代码注释的Vert.x源码