最近在做一个呼入智能客服机器人的项目,遇到了不少挑战,尤其是在高并发场景下,系统响应延迟飙升、资源占用居高不下,甚至偶尔出现对话上下文'断片'的情况。今天就来复盘一下我们是如何通过架构设计和性能优化,让这个机器人变得既'聪明'又'抗压'的。

背景痛点:当流量洪峰来袭
我们最初设计的系统,在面对日常流量时表现尚可。但一到促销活动或业务高峰期,问题就集中爆发了。
- 并发处理瓶颈:最直观的问题是响应延迟。传统的同步阻塞式处理(比如用 Spring MVC 的
@RestController),每个用户请求都会占用一个 Servlet 容器线程。当并发请求数超过线程池大小时,新请求只能排队等待,导致 TP99(99% 的请求响应时间)指标急剧恶化,用户感觉机器人'变卡了'。 - 对话上下文丢失:智能客服的核心在于多轮对话。我们需要在短时间内记住用户说了什么、机器人回复了什么,以维持对话逻辑。在高并发下,如果对话状态管理不当(比如用简单的内存 Map),很容易出现状态被覆盖或清理,导致机器人'失忆',回答得牛头不对马嘴。
- 资源利用率不均:系统内不同模块负载差异大。自然语言理解(NLU)模块计算密集,知识库检索模块 I/O 密集。同步架构下,它们被强耦合在一起,一个模块慢了,整个链路都得等着,CPU 和 I/O 资源无法被高效、独立地利用。
这些问题迫使我们重新思考整个系统的架构。
技术选型:从同步阻塞到异步事件驱动
我们首先做了技术选型的对比测试。用相同的业务逻辑,分别实现同步阻塞(Spring MVC + Tomcat)和异步事件驱动(Spring WebFlux + Netty)两种架构,并在同样硬件环境下进行压测。
结果很清晰:在达到每秒 5000 查询(QPS)时,同步架构的 TP99 已经超过 500 毫秒,而异步架构仍能保持在 200 毫秒以内。异步非阻塞的模型能够用更少的线程(甚至是单线程)处理更多的并发连接,特别适合 I/O 密集型且需要保持大量并发生命周期的场景——比如持续的客服对话。
因此,我们最终的技术栈确定为:
- Spring WebFlux:作为反应式编程框架,提供非阻塞、背压(Backpressure)支持的 Web 层。
- Apache Kafka:作为异步消息队列。将用户的每一次对话请求作为一个事件发布,由后端的各个消费者服务(NLU、对话管理、知识检索)异步处理,实现解耦和削峰填谷。
- Redis:用于存储分布式对话状态,利用其高性能和丰富的数据结构(如 Hash)来管理上下文。
- Spring Cloud & Resilience4j:用于服务治理,包括熔断器(CircuitBreaker)、限流和重试,提升系统韧性。
这个组合让我们的系统从'被动处理请求'转变为'主动消费事件',资源调度更加灵活。
核心实现:领域驱动与异步集成
1. 领域模型划分(DDD)
我们使用领域驱动设计(DDD)来厘清复杂业务,核心限界上下文包括:
- 意图识别上下文:负责接收用户原始语句,调用 NLU 引擎(如 Dialogflow、Rasa 或自研模型)解析出用户意图(Intent)和关键参数(Entities)。
- 对话管理上下文:核心是一个状态机(State Machine)。它根据当前对话状态和 NLU 解析结果,决定下一步该执行什么动作(如询问、确认、调用 API、结束对话),并更新对话状态。
- 知识检索上下文:当对话需要查询知识库(FAQ、文档)时,由该上下文负责向量检索、关键词匹配等操作。
每个上下文都是一个独立的微服务,通过 Kafka 消息进行通信。
2. Spring Boot 集成与关键代码
以意图识别服务调用 Dialogflow 为例,我们采用异步方式避免阻塞。
首先,定义异步服务类:

