Skip to main content

线程相关

tip

这篇主要介绍框架线程相关的内容。

介绍

框架为开发者提供了线程相关的定制与扩展,开发者可以根据项目需求及特点,来制定符合自身项目的业务线程编排。 框架默认的线程实现比较简单,在接收到 Aeron 消息后,会将消息派发到线程执行器中。 如有特殊业务的,开发者可以在这个阶段进行线程编排。

An

自定义线程编排的优点与应用场景

默认的策略是通过 userId 获取对应的执行线程,在新开服时,这种默认策略比较好,比较均衡。

但随着时间的推移,服务器总会有用户流失。 此时,我们就可以使用自定义策略,使用其他的业务标识来分配执行线程。 这样依旧能做到线程的均衡执行,从而避免部分线程执行器空闲,部分忙绿的情况。

以上只是其中一个场景的举例,更多的业务场景请自行分析后做定制。 比如,我们可以让某个涉及 IO 的 action 在虚拟线程中执行,以避免阻塞用户线程。

ThreadExecutorRegion

An

框架内置了 3 个线程执行器管理域,分别是

  1. UserThreadExecutorRegion,用户线程执行器管理域。
  2. UserVirtualThreadExecutorRegion,用户虚拟线程执行器管理域。
  3. SimpleThreadExecutorRegion,简单的线程执行器管理域。

线程执行器管理域中持有多个线程执行器,每个线程执行器中只有一个线程,而线程执行器的主要作用有两点

  1. 消费任务。
  2. 规避并发(因为每个线程执行器是单线程的,所以可以很好的避免并发问题)。

线程执行器管理域的线程执行器具体数量是不大于 Runtime.getRuntime().availableProcessors() 的 2n。 当 availableProcessors 的值分别为 4、8、12、16、32 时,对应的数量则是 4、8、8、16、32。

availableProcessors 值Executor[] 数组实际值。(线程执行器)
44
88
128
1616
3232

这样设计的好处是可以通过 userId 快速获取对应的线程执行器

final class UserThreadExecutorRegion extends AbstractThreadExecutorRegion {
...
@Override
public ThreadExecutor getThreadExecutor(long userId) {
return this.threadExecutors[(int) (userId & this.executorLength)];
}
}

如何获取 ExecutorRegion

通过 ExecutorRegion 我们可以获取不同的线程执行器管理域

  1. UserThreadExecutorRegion
  2. UserVirtualThreadExecutorRegion
  3. SimpleThreadExecutorRegion
long userId = ...;
BarSkeleton barSkeleton = ...
ExecutorRegion executorRegion = barSkeleton.executorRegion;

executorRegion.getUserThreadExecutor(userId);
executorRegion.getUserVirtualThreadExecutor(userId);
executorRegion.getSimpleThreadExecutor(userId);

UserThreadExecutorRegion

该线程执行器主要用于处理 action 请求。

UserThreadExecutorRegion 是用户线程执行器管理域,而管理域下则会有多个线程执行器。 每个用户会与其中一个线程执行器关联绑定,以避免并发问题。 由于一个线程执行器会被多个用户使用,所以不要在此线程执行器中做耗时的阻塞任务,以免阻塞其他玩家。


Usage Examples

flowContext.executeUser(() -> {
// your code
});

// or
ExecutorRegion executorRegion =...
executorRegion.getUserThreadExecutor(userId);

UserVirtualThreadExecutorRegion

UserVirtualThreadExecutorRegion 是用户虚拟线程执行器管理域,该线程执行器主要用于处理 io 相关的耗时业务,如 DB 入库。


Usage Examples

flowContext.executeVirtual(() -> {
// your code
});

// or
ExecutorRegion executorRegion =...
executorRegion.getUserVirtualThreadExecutor(userId);

SimpleThreadExecutorRegion

SimpleThreadExecutorRegion 是简单的线程执行器管理域,该执行器与 UserThreadExecutorRegion 类似。 如果业务是计算密集型的,又不想占用 UserThreadExecutorRegion 线程资源时,可使用该执行器。


Usage Examples

ExecutorRegion executorRegion =...
var threadExecutor = executorRegion.getSimpleThreadExecutor(userId);
threadExecutor.executeTry(() -> {
// your code
});

如何扩展

自定义 SkeletonThreadPipeline

public final class MySkeletonThreadPipeline implements SkeletonThreadPipeline {
@Override
public void execute(BarSkeleton barSkeleton, FlowContext flowContext) {
// Your code
}
}

添加 SkeletonThreadPipeline 扩展

class OneApplication {
static void main() {
RunOne runOne = new RunOne();

var netServerBuilder = runOne.getNetServerBuilder();
netServerBuilder.setSkeletonThreadPipeline(new MySkeletonThreadPipeline());

runOne
...
.startup();
}
}

小结

框架解决了单个玩家的并发问题,即使玩家重新登录后,也会使用相同的线程来消费业务。 如果开发者有特殊业务的,可以通过重写 SkeletonThreadPipeline 接口来扩展。