Skip to content
Embabel 文档版本 0.3.0-SNAPSHOT 源快照 15f2421d 更新于 2026-09-29

44 · 异步模式与 Java 25(Asynchronous Mode and Java 25)

异步配置定义在 AsyncConfiguration.kt 中。

线程模型(Threading Model)

Embabel 为所有 Agent 操作使用专用执行器,并采用基于继承的线程模型。

升级注意 —— 在此版本之前,Embabel 默认设置 spring.threads.virtual.enabled=true, 强制所有应用使用虚拟线程。该行为已被移除,以避免干扰宿主应用的线程配置。 现在 Embabel 从 spring.threads.virtual.enabled 继承线程模型(未设置时默认为平台线程)。 要启用虚拟线程,请在 application.properties 中显式设置 spring.threads.virtual.enabled=true。

配置属性

两个正交的属性控制 Embabel 的线程行为:

  • 线程模型继承:embabel.agent.platform.threading.override
    • false(默认):从 spring.threads.virtual.enabled 继承线程模型;
    • true:翻转线程模型(virtual → platform,或 platform → virtual)。
  • 执行器共享:embabel.agent.platform.threading.shared
    • false(默认):为 Embabel 操作创建隔离的执行器;
    • true:当线程模型一致时,共享应用的 applicationTaskExecutor bean。

在 agent-platform.properties 中配置:

properties
# 翻转继承来的线程模型(virtual→platform,platform→virtual)
embabel.agent.platform.threading.override=false

# 当应用与 Embabel 使用相同线程模型时,共享应用的执行器
embabel.agent.platform.threading.shared=false

行为矩阵(Behavior Matrix)

应用线程overridesharedEmbabel 线程模型执行器类型
PlatformfalsefalsePlatform(继承)隔离的平台执行器(默认)
PlatformfalsetruePlatform(继承)共享应用的平台执行器
PlatformtruefalseVirtual(翻转)隔离的虚拟执行器
PlatformtruetrueVirtual(翻转)隔离的虚拟执行器(模型不同)
VirtualfalsefalseVirtual(继承)隔离的虚拟执行器
VirtualfalsetrueVirtual(继承)共享应用的虚拟执行器
VirtualtruefalsePlatform(翻转)隔离的平台执行器
VirtualtruetruePlatform(翻转)隔离的平台执行器(模型不同)

关键点:shared=true 只有在应用与 Embabel 使用相同线程模型时(platform/platform 或 virtual/virtual)才启用执行器共享; 线程模型不一致时,Embabel 总是创建隔离的执行器。

注意:虚拟线程需要 Java 21+。在 Java 17–20 上,即使请求了虚拟线程,Embabel 也会自动回退到平台线程。

框架在 Agent 调用、Agent 动作、工具循环、Shell 命令等场景中一致地使用 Asyncer 抽象。

Java 25 的影响(Java 25 Implications)

在 Java 25 之前,JVM 读取容器 CPU 限额时不精确 —— 它常常看到宿主机的全部 CPU 数,而不是容器的 cgroup 配额。 Java 25 修正了这一点,会准确遵循 cgroup CPU 限额(例如 Kubernetes 的 resources.limits.cpu)。 相关 OpenJDK 工单:JDK-8362881。

当在容器(Docker / Kubernetes)里用 Java 25 运行 Embabel 时,所有 Agent 动作会串行执行而不是并行。 原因正是 Java 25 更准确的 cgroup CPU 检测:在受 CPU 限制的容器里它报告 availableProcessors() = 1。 由于 ForkJoinPool.commonPool 会据此决定并行度,实际并行度降到 1,于是所有并发 Agent 操作被串行化。

JVM 版本availableProcessors()Common pool 并行度行为
25 之前宿主机 CPU 数(例如 8)7动作并行执行
25容器 CPU 限额(例如 1)1(最小值)动作串行执行

另外请注意:现代(1.3 之后)Kotlin 协程在 Dispatchers.Default 与 Dispatchers.IO 上的实现, 背后是 Kotlin 自己的协程调度器,而不是 Java 的 ForkJoinPool.commonPool()。

为什么 Embabel 是安全的

  • Asyncer 抽象 —— 所有并行执行都走 ExecutorAsyncer;
  • 专用执行器 —— 使用专用执行器(虚拟线程或无界平台线程),不是 ForkJoinPool;
  • 无界执行 —— 配置为无界,以支持并行动作执行;
  • 协程默认值 —— 生产代码使用 Dispatchers.IO,而不是 Dispatchers.Default。

如果用户报告 Java 25 上的串行化问题,建议检查:

  • 自定义代码里使用了 CompletableFuture.supplyAsync() 而未指定执行器;
  • 自定义代码使用了 Dispatchers.Default(Kotlin 协程);
  • Spring 配置有误 —— applicationTaskExecutor 未正确配置;
  • 依赖 ForkJoinPool.commonPool 的第三方库。

变通方案(如确需)

尽管 Embabel 核心是安全的,但若你有受影响的自定义代码,可以采取以下设置:

方案 1:设置容器 CPU 限额

yaml
# Kubernetes
resources:
  limits:
    cpu: "4"
  requests:
    cpu: "2"

方案 2:覆盖 ForkJoinPool 并行度

bash
java -Djava.util.concurrent.ForkJoinPool.common.parallelism=4 -jar agent.jar

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。