跳到主要内容

虚拟线程

虚拟线程(Virtual Thread)是 Java 21 提供的轻量级线程模型。它保留了传统“一个任务一个线程”的直观编程方式,但把线程调度成本从操作系统线程切换到了 JVM 层面,因此更适合高并发、阻塞型、IO 密集型业务。

对于微语客服系统这类同时连接用户、坐席、第三方渠道、AI 服务、呼叫系统、知识库抓取任务的应用来说,大量请求并不是一直在消耗 CPU,而是在等待网络、数据库、Redis、WebSocket、IMAP、第三方接口返回。虚拟线程的价值,正是在这些“等待很多、并发很高”的场景里体现出来。

微语客服系统中的使用方式​

当前项目已经在多个模块中实际使用虚拟线程,不是停留在实验阶段。

1. 作为 Spring 异步任务的默认执行模型​

系统启动配置中已经开启:

spring.threads.virtual.enabled=true

同时,在核心模块中定义了统一异步执行器,将 Spring 的 @Async、applicationTaskExecutor 等入口适配到虚拟线程执行器上。这样做的结果是:

  • 事件监听器
  • 异步通知
  • 后台导入任务
  • 非关键链路的业务回调

都可以继续沿用 Spring 现有编程模型,而不需要把整套代码改写成响应式风格。

2. 用在客服系统里典型的阻塞型 IO 场景​

项目里已经有多类真实业务直接使用了 Executors.newVirtualThreadPerTaskExecutor() 或 Thread.startVirtualThread():

  • 知识库整站抓取:网站抓取服务会并发访问大量页面,任务大部分时间耗在 HTTP 请求和 HTML 解析等待上,适合用虚拟线程提升并发抓取能力。
  • 呼叫中心与 FreeSWITCH ESL:事件回调、后台任务和连接处理包含大量网络等待,使用虚拟线程后可以保留同步写法,同时减少传统线程池调参压力。
  • 企业微信、飞书等渠道集成:收到回调后,系统会异步同步消息或启动长连接,避免阻塞 Web 请求线程和 Spring 初始化流程。
  • AI 流式输出:例如 SSE 场景下,虚拟线程用于承载一次流式生成过程,代码仍然保持顺序执行风格。
  • 邮件 IDLE 监听、Janus/WebRTC 客户端:这类长连接和等待型任务非常适合交给虚拟线程处理。
  • Redis 过期事件后的异步超时处理:例如会话转接超时后的补偿逻辑,可以快速起一个虚拟线程异步执行,不阻塞事件分发线程。

3. 在需要“顺序语义”的地方配合使用​

项目里并不是所有地方都直接改成“无限并发”。例如呼叫模块里有些回调执行器仍然保留了单线程语义,只是底层线程工厂换成了虚拟线程。这样既能保持消息处理顺序,又能降低线程资源占用。

这也是当前项目使用虚拟线程的一个原则:

  • 需要高并发等待时,用每任务一个虚拟线程。
  • 需要顺序消费时,保留顺序模型,但让线程更轻。
  • 需要显式限流、排队或背压时,继续使用传统线程池。

为什么微语客服系统适合虚拟线程​

微语客服系统的业务结构决定了它天然适合虚拟线程。

1. 系统里有大量等待型任务​

客服系统看起来业务复杂,但很多链路的核心特征并不是高 CPU 计算,而是大量等待:

  • 等待用户消息
  • 等待第三方渠道回调
  • 等待 AI 服务返回
  • 等待 FreeSWITCH / Janus / WebSocket 消息
  • 等待 Redis、数据库、邮件服务器响应

如果这类任务全部压在平台线程上,就容易出现线程数膨胀、线程池排队、上下文切换开销变大等问题。虚拟线程更适合承接这种高并发等待。

2. 客服业务经常出现突发并发​

营销活动、渠道投放、机器人批量接入、工单导入、知识库抓取、呼叫事件风暴,都会造成瞬时任务数上升。传统线程池通常需要提前预估核心线程数、最大线程数和队列大小,但实际业务峰值很难精确预测。

虚拟线程不能替代容量规划,但可以显著降低“线程资源先成为瓶颈”的概率,让系统更从容地承接突发流量。

3. 可以保留现有 Spring MVC / JPA 编程模型​

微语客服系统是一个典型的 Spring Boot 多模块应用,已经存在大量同步式服务、事件监听器、JPA 访问、第三方 SDK 调用。

如果为了提升并发能力而全面改造成响应式架构,成本很高,迁移风险也大。虚拟线程提供了一条更现实的路径:

  • 保留已有代码结构
  • 保留事务和服务层模型
  • 保留绝大多数同步 API 写法
  • 在 Java 21 上获得更好的并发承载能力

这对一个持续演进的客服平台来说,是投入产出比很高的方案。

使用虚拟线程带来的好处​

1. 提升高并发 IO 场景下的吞吐能力​

在大量外部调用、长连接等待、异步事件处理并存的情况下,虚拟线程可以让系统同时挂起更多任务,而不必为每个等待中的任务绑定一个昂贵的平台线程。

2. 降低线程池调参复杂度​

传统线程池经常要在核心线程数、最大线程数、队列长度之间做权衡。参数设小了容易堆积,设大了又可能引发线程切换和内存压力。虚拟线程并不消除所有调优工作,但明显减轻了线程数量配置的负担。

3. 让代码继续保持直观​

相比回调地狱或大范围响应式改造,虚拟线程允许我们继续用顺序式代码描述业务流程。对于客服系统这种流程复杂、异常处理分支较多的业务,这一点非常重要,能降低维护成本。

4. 更适合长连接和外部系统集成​

客服平台天然会接很多外部系统:企业微信、飞书、邮件、AI、语音网关、WebRTC 网关等。只要这些链路以阻塞等待为主,虚拟线程通常都比“一味加大平台线程池”更合适。

压测与性能收益建议​

虚拟线程是否带来明显收益,最终还是要用压测数据说话。对于微语客服系统,建议不要只做单一接口的基准测试,而要围绕真实业务链路做对比。

1. 建议优先压测这些场景​

  • 客服消息接入链路:模拟访客消息、渠道消息、机器人消息同时进入系统。
  • AI 流式回复链路:重点观察 SSE 长连接数量增长时的稳定性。
  • 呼叫中心事件链路:模拟 FreeSWITCH / ESL 事件高频进入时的处理能力。
  • 知识库整站抓取:对比多页面抓取时的吞吐量与资源占用。
  • 邮件监听和第三方回调:观察大量等待型任务同时存在时的线程与内存表现。

2. 建议的对比方式​

建议至少保留两组测试:

  • 一组使用传统线程池配置。
  • 一组启用当前虚拟线程配置。

对比时尽量保证其他变量一致,例如:

  • 相同 JDK 版本
  • 相同数据库与 Redis 配置
  • 相同外部依赖响应时间
  • 相同 JVM 堆大小与机器规格

这样才能把差异更多聚焦在线程模型本身,而不是环境抖动。

3. 建议重点观察的指标​

不要只看 QPS,建议至少一起观察:

  • 平均响应时间
  • P95 / P99 延迟
  • 并发连接数或并发任务数
  • JVM 线程数量
  • CPU 使用率
  • 堆内存与非堆内存变化
  • Full GC / Young GC 次数
  • 请求超时率与错误率

对于客服系统来说,很多时候真正重要的不是峰值吞吐,而是在高并发等待场景下,系统是否还能保持稳定、延迟是否恶化、错误率是否上升。

4. 在微语客服系统中,预期可以看到哪些收益​

如果压测场景以阻塞等待为主,通常可以重点关注这些变化:

  • 相同机器规格下,可承载更多并发等待任务。
  • 在长连接、回调、外部接口等待同时存在时,线程资源更不容易先耗尽。
  • 高并发场景下,线程池排队导致的尾延迟恶化会有所缓解。
  • 某些原本需要复杂线程池调参的业务,配置成本会下降。

需要强调的是,这里的“收益”更常体现为系统承载力和稳定性改善,而不一定是所有场景下 CPU 利用率或单请求耗时都直接大幅下降。

5. 压测结论应如何解读​

如果测试结果显示收益明显,通常说明该链路确实是典型的 IO 密集型、等待型业务,适合继续沿用虚拟线程。

如果收益不明显,常见原因有:

  • 该场景本身更偏 CPU 密集型
  • 瓶颈其实在数据库、Redis 或第三方接口
  • 下游系统容量太小,线程模型不是主要矛盾
  • 业务中仍有显式串行点或锁竞争

因此,虚拟线程压测的目标不应是“证明它在所有场景都更快”,而是识别哪些链路最适合它,哪些链路仍然需要传统限流、缓存、批处理、异步削峰或架构优化。

我们为什么不是所有地方都改成虚拟线程​

虚拟线程很适合当前系统,但它不是银弹。

1. CPU 密集型任务收益有限​

如果任务本身主要消耗 CPU,例如大规模复杂计算、重度编解码、长时间占用核心的算法处理,那么虚拟线程并不会凭空提升算力。

2. 需要明确背压的场景仍应保留线程池​

项目里仍然保留了一些传统线程池配置,例如需要限制并发度、保护下游系统、明确拒绝策略或保持固定消费速率的场景。这里关注的不是“线程轻不轻”,而是“系统边界能不能稳住”。

3. 异步后要注意上下文传播​

在部分切面代码里已经明确标注:如果异步执行后还依赖 HttpServletRequest 等请求上下文,就不能简单地把整段逻辑直接丢到异步线程中。这个限制不是虚拟线程独有的问题,而是所有异步模型都需要注意的上下文边界。

小结​

在微语客服系统中,虚拟线程并不是一个“为了追新而启用”的特性,而是围绕系统实际特点做出的工程选择:

  • 系统存在大量高并发、阻塞型、等待型任务。
  • 系统集成了大量外部通道和长连接能力。
  • 系统希望在不推翻现有 Spring 架构的前提下提升并发承载能力。

因此,我们选择在 Java 21 基础上引入虚拟线程,并把它落到 Spring 异步执行器、网站抓取、呼叫中心、AI 流式输出、渠道回调、邮件监听、WebRTC 网关客户端等真实场景中。

对于客服系统这类典型的 IO 密集型平台,虚拟线程是一种非常务实的技术升级:它不要求彻底重写架构,却能显著改善系统在高并发等待场景下的可扩展性和维护性。

这页文档对您有帮助吗?