☰
QuickBlue:基于JDK 21与Spring Cloud的AI应用底座架构设计与实践
2026/10/8 5:57:42 网站建设 项目流程

1. 从一堆零散服务到统一底座:QuickBlue 到底想解决什么问题

第一次听到“QuickBlue”这个名字,很多人会以为是某个新出的前端 UI 库或者监控面板。实际上,它要处理的是企业里越来越普遍的一种困境:AI 能力已经能跑起来了,但跑得七零八落。模型调用散落在各个业务代码里,提示词硬编码在 Java 类中,向量检索、会话管理、限流降级各写一套,最后没人说得清线上到底有几个版本在同时服务。

QuickBlue 的定位就是把这些零散能力收拢到一个统一的“AI 应用底座”上。底座这个词很关键,它不是业务系统本身,而是业务系统下面那层承重结构。就像盖楼时先打地基、立柱子,之后每层楼怎么隔间、怎么装修可以自由发挥,但承重体系是统一的。QuickBlue 想做的,就是让企业在接入大模型、构建 AI 应用时,不必每次都从零搭一套调用框架、鉴权体系、流量治理和可观测链路。

从热搜词能看出,大家关注的点集中在微服务、JDK 21、Spring Cloud这几块。这说明 QuickBlue 并不是一个孤立的 AI 工具,而是把 AI 能力嵌入到已有的微服务治理体系里。企业已经有了一套基于 Spring Cloud 的服务集群,现在要往里面加 AI 能力,最怕的就是另起炉灶。QuickBlue 的思路是复用微服务那套注册发现、配置中心、网关路由、熔断限流的成熟机制,把 AI 调用当成一种特殊的服务能力来治理。

那为什么非要一个“底座”,而不是每个团队自己封装一个 SDK 就完事?我踩过的坑很典型:三个业务团队各自封装了模型调用,A 团队用 OkHttp 直接发请求,B 团队用 WebClient 做流式,C 团队在 Spring AI 上又包了一层。结果就是日志格式不统一,超时时间各不相同,出了故障排查要翻三套代码。更麻烦的是,当公司要统一换一个模型供应商时,三个团队要改三遍,测试三遍,上线三遍。底座的价值就在这里——把变化点收敛到一层,上层业务只面向稳定接口编程。

QuickBlue 适合谁来参考?如果你是后端架构师,正在规划公司级 AI 能力接入方案,那它的分层思路值得细看。如果你是 Spring Cloud 微服务的维护者,被要求“把 AI 能力接进来但别搞乱现有体系”,那它的集成方式能直接抄作业。如果你只是刚接触 AI 应用开发,想理解一个生产级底座长什么样,那从它的模块划分入手,比直接看模型 API 文档更有全局感。

2. 拆开 QuickBlue 的骨架:分层设计与技术选型逻辑

2.1 为什么是“底座”而不是“框架”或“平台”

框架通常指一套开发范式,比如 Spring Boot 让你按它的方式写代码;平台往往带控制台、带运维界面,偏向管理侧。底座介于两者之间,它既提供开发时依赖的公共能力,又承担运行时的统一治理职责,但不强制你改变业务代码的组织方式。QuickBlue 选择“底座”这个定位,我理解是为了降低接入成本——业务团队不需要把现有代码推倒重来,只需要把原来直连模型的那几行替换成对底座客户端的调用。

这个选择背后有个很现实的考量:企业里跑着的 AI 相关代码,很多是业务同学在赶需求时快速写出来的,能跑就行。你让他为了接入底座去重构整个模块,他肯定抵触。但如果底座提供的是一个和原来调用方式几乎一样的客户端,只是底层换成了统一通道,那推广阻力就小得多。QuickBlue 在接口设计上明显照顾了这种“无感迁移”的需求。

2.2 JDK 21 带来的底层能力变化

热搜词里 JDK 21 出现得很频繁,这不是偶然。JDK 21 是 LTS 版本,虚拟线程正式转正,这对 AI 应用底座来说是个大利好。AI 调用有个特点:大量时间花在等待模型响应上,尤其是流式输出场景,一个请求可能持续几十秒。传统平台线程模型下,每个请求占一个线程,并发一高线程池就爆。虚拟线程让“一个请求一个线程”的简单模型重新变得可行,不用再为了省线程去写复杂的响应式代码。

QuickBlue 如果基于 JDK 21 构建,最直接的收益在流式对话和批量推理这两个场景。流式对话需要长时间持有连接,虚拟线程的挂起成本极低,可以轻松支撑数万并发会话。批量推理时,底座可以给每个子任务分配一个虚拟线程,代码写起来是同步阻塞的风格,但实际执行效率接近异步。我实测过类似方案,同样的硬件下,虚拟线程版本比传统线程池版本在 IO 密集型任务上吞吐能高出三到五倍,而且代码可读性好了不止一个档次。

注意:虚拟线程虽好,但不要用它跑 CPU 密集型任务。AI 底座里做向量计算、文本预处理这类活,还是应该交给固定大小的平台线程池,否则虚拟线程的调度开销反而拖慢整体。

2.3 Spring Cloud 生态的取舍与适配

Spring Cloud Alibaba 停更的消息在社区里传了很久,这让很多企业在选型时犹豫。QuickBlue 如果深度绑定 Spring Cloud,就必须面对这个现实:用原生 Spring Cloud 还是继续用 Alibaba 套件?从热搜词看,Sentinel、Nacos 这些 Alibaba 组件依然在被大量使用,说明存量系统迁移成本很高。

QuickBlue 比较务实的做法是抽象出服务发现、配置管理、流量治理这几个接口,底层实现可以插拔。今天用 Nacos 做注册中心,明天想换 Consul,改配置就行,不用动业务代码。Sentinel 做限流熔断,如果将来要换 Resilience4j,也是换实现类的事。这种“面向接口治理”的思路,比直接依赖某个具体组件要稳得多。我在实际项目里吃过亏:早期图省事直接调 Nacos API,后来要换注册中心时发现几十个地方要改,教训很深刻。

2.4 微服务拆分粒度对 AI 底座的影响

微服务拆分是个老话题,但 AI 场景下有了新变化。传统业务微服务按领域拆分,订单服务、用户服务、库存服务,边界相对清晰。AI 能力怎么拆?一种做法是拆出一个“AI 网关服务”,所有模型调用都走它;另一种是每个业务服务自己内嵌 AI 客户端,底座只提供 SDK。

QuickBlue 看起来倾向于前者,但做了更细的层次划分。底座本身可能包含模型路由服务、提示词管理服务、会话状态服务、向量检索服务等。这些服务各自独立部署,通过内部 RPC 或消息队列协作。这样拆的好处是,模型路由可以独立扩缩容,提示词管理可以独立发版,不会因为改一个提示词模板就重启整个 AI 网关。但代价是调用链路变长,一次对话可能经过三四个内部服务,延迟和故障点都增加了。所以底座内部的服务间通信必须用高性能的 RPC,不能再用 HTTP 绕一圈。

3. 核心模块逐个拆:从模型接入到流量治理的实操细节

3.1 模型接入层:统一协议与多供应商适配

模型接入层是底座最靠近外部的一层,也是最容易变的一层。今天接这家模型,明天接那家,接口协议、鉴权方式、流式格式都不一样。QuickBlue 在这一层的核心任务是定义一套内部统一协议,把外部差异屏蔽掉。

具体怎么做?首先定义一个ModelRequest和ModelResponse,包含消息列表、温度、最大 token 数这些通用参数。然后为每个模型供应商写一个适配器,把内部请求翻译成供应商要求的格式,再把供应商的响应翻译回内部格式。流式场景下,适配器还要把不同格式的 SSE 事件统一成内部的事件流。

这里有个细节很容易被忽略:不同模型对“系统提示词”的支持程度不一样。有的模型有专门的 system 角色,有的只能把系统提示词拼在用户消息前面。适配器要处理这种差异,让上层业务不用关心。我见过一个项目,业务代码里到处判断“如果是某模型就拼字符串,否则用 system 角色”,这种逻辑散落各处,后来加新模型时改得痛不欲生。QuickBlue 把这种判断收在适配器里,是正确做法。

实操心得:适配器里一定要做超时和重试的差异化配置。不同模型的响应时间差异很大,用一个全局超时值要么误杀快模型,要么拖死慢模型。建议按模型维度配置超时,并且重试只对幂等的非流式请求开启,流式请求重试会导致重复输出。

3.2 提示词管理:从硬编码到可配置

提示词硬编码在 Java 类里,是 AI 应用早期最常见的反模式。改一个标点符号都要走发版流程,产品经理改一版文案,开发就要跟着上一次线。QuickBlue 把提示词管理独立出来,支持在配置中心或数据库里维护模板,业务代码通过模板 ID 引用。

模板引擎的选择也有讲究。简单的占位符替换用String.format或MessageFormat就够了,但如果提示词里有条件分支、循环、变量默认值,就需要更强大的模板引擎。我倾向于用轻量的模板语法,比如{{variable}}这种,不要引入太重的模板引擎,否则学习成本和渲染开销都上去了。

提示词版本管理是另一个关键点。同一个模板 ID 下可能有多个版本,线上跑的是 v3,测试环境在验证 v4。底座要支持按环境、按租户、按灰度比例来路由到不同版本。这样产品经理改提示词时,可以先在小流量上验证效果,确认没问题再全量。没有这套机制,每次改提示词都是一次全量赌博。

3.3 会话与上下文管理:状态放哪里

多轮对话需要保存上下文,这个状态放哪里是个架构决策。放内存最简单,但服务重启就丢,多实例部署时还会串会话。放 Redis 是常见做法,但要注意序列化方式和过期策略。会话数据可能很大,尤其是长对话,全部塞进一个 Redis value 里,读写放大很严重。

QuickBlue 如果做得好,应该支持会话的分层存储:最近几轮对话放 Redis,更早的历史归档到数据库或对象存储。读取时先查 Redis,命中不了再回源。这样既保证了热数据的低延迟,又控制了内存占用。另外,会话的过期时间要可配置,不同业务场景对上下文保留时长的要求不一样,客服机器人可能只需要保留最近半小时,个人助理可能需要保留几个月。

还有一个容易被忽视的问题:会话并发写。同一个会话如果同时有两个请求在写,后写的可能覆盖先写的。底座需要提供乐观锁或分布式锁机制,保证会话更新的原子性。我见过线上出现对话内容错乱,排查半天发现是两个请求并发更新会话导致的。

3.4 流量治理:限流、熔断与降级在 AI 场景的特殊性

AI 调用的流量治理和传统接口有很大不同。传统接口的耗时通常在几十到几百毫秒,AI 调用动辄几秒到几十秒。这意味着同样的并发数下,AI 服务占用的连接和线程资源要多得多。限流策略不能简单照搬 QPS 限流,还要考虑并发连接数限流。

熔断策略也要调整。传统接口失败率超过阈值就熔断,但 AI 调用失败的原因很复杂:可能是模型服务过载,可能是网络抖动,也可能是提示词触发了内容安全策略。不同原因应该有不同的处理方式。模型过载可以重试或降级到备用模型,内容安全拦截则不应该重试,直接返回用户提示。

降级方案在 AI 场景下尤其重要。当主模型不可用时,是降级到更小的模型,还是返回缓存结果,还是直接告诉用户“服务繁忙”?这取决于业务容忍度。底座应该提供降级策略的配置能力,让业务方自己决定。我建议至少配置两级降级:一级是切换到备用模型,二级是返回兜底话术。不要小看兜底话术,它能让用户在系统故障时依然得到有意义的反馈,而不是一个冰冷的错误码。

3.5 可观测性:日志、指标与链路追踪

AI 应用的可观测性比传统应用更难做,因为多了模型这个黑盒。一次调用出了问题,可能是业务代码的 bug,可能是提示词写得不好,可能是模型本身抽风,也可能是网络问题。没有完善的观测数据,排查就是盲人摸象。

QuickBlue 需要在三个层面埋点:请求层面记录完整的输入输出(注意脱敏)、模型层面记录 token 消耗和响应延迟、系统层面记录资源使用情况。链路追踪要把一次用户请求经过的所有内部服务串联起来,包括模型路由、提示词渲染、会话读写、向量检索等环节。

日志脱敏是个必须重视的问题。AI 对话内容可能包含用户隐私,直接打到日志里风险很大。底座应该提供脱敏规则配置,比如自动识别手机号、身份证号并替换。同时,日志的存储周期也要控制,不能无限期保留。

4. 把 QuickBlue 跑起来:从零搭建的实操路径

4.1 环境准备与依赖版本锁定

假设我们要在一台开发机上把 QuickBlue 的最小可用版本跑起来。首先明确基础环境:JDK 21 是必须的,因为要用虚拟线程。Maven 或 Gradle 选一个顺手的,我习惯用 Maven,依赖管理直观。注册中心和配置中心,开发环境可以用 Nacos 的单机模式,一条 Docker 命令就能起。

依赖版本锁定是第一步。Spring Boot 3.2 以上才完整支持 JDK 21 的虚拟线程,Spring Cloud 版本要对应 2023.0.x 系列。这里有个坑:Spring Cloud Alibaba 的版本和 Spring Cloud 版本有严格的对应关系,选错了启动就报兼容性错误。我建议直接查官方版本对应表,不要凭感觉选。

<properties> <java.version>21</java.version> <spring-boot.version>3.2.5</spring-boot.version> <spring-cloud.version>2023.0.1</spring-cloud.version> <spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version> </properties>

数据库方面,会话和提示词模板需要持久化,MySQL 8.0 是稳妥选择。Redis 用于缓存和会话热数据,版本 7.x 即可。向量检索如果要用,可以选 Milvus 或 PgVector,开发阶段用 PgVector 更省事,不用额外维护一个向量数据库。

4.2 服务拆分与模块初始化

QuickBlue 底座本身建议拆成这几个模块:quickblue-gateway负责统一入口和鉴权,quickblue-model-router负责模型适配和路由,quickblue-prompt负责提示词管理,quickblue-session负责会话状态,quickblue-common放公共依赖和工具类。

初始化时用 Spring Initializr 生成骨架,每个模块选上需要的依赖。网关模块选 Spring Cloud Gateway,模型路由模块选 WebFlux 或 MVC 加虚拟线程,会话模块选 Spring Data Redis 和 MyBatis-Plus。注意不要所有模块都引入全量依赖,按需引入,否则启动慢、内存占用高。

模块间的调用关系要提前定好。网关只调用模型路由,模型路由调用提示词和会话,会话调用 Redis 和数据库。不要让网关直接调会话,否则链路混乱。调用方式统一用 OpenFeign 或 Dubbo,我倾向于 Feign,和 Spring Cloud 集成更顺滑。

4.3 模型适配器的编写与注册

写一个模型适配器,核心是实现统一接口。假设我们定义ModelAdapter接口,包含chat和streamChat两个方法。然后为每个模型供应商写实现类,比如OpenAIAdapter、QwenAdapter、DeepSeekAdapter。

适配器里要做的事情包括:把内部ModelRequest转成供应商格式、设置鉴权头、处理流式响应、把供应商的错误码转成内部错误码。流式响应的处理最麻烦,不同供应商的 SSE 格式不一样,有的用data:前缀,有的用 JSON Lines。适配器要统一成内部事件流,上层业务只关心onMessage、onComplete、onError三个回调。

适配器写完后要注册到路由表里。可以用 Spring 的@Component自动扫描,也可以手动注册到ModelRouter里。我建议用配置驱动的方式,在配置文件里指定模型名称和适配器类的映射关系,这样加新模型不用改代码,加个配置就行。

quickblue: models: - name: gpt-4o adapter: openai endpoint: https://api.example.com/v1 timeout: 60s - name: qwen-max adapter: qwen endpoint: https://dashscope.example.com/v1 timeout: 30s

4.4 提示词模板的存储与渲染

提示词模板存在数据库里,表结构至少包含:模板 ID、版本号、内容、变量定义、创建时间、状态。变量定义用 JSON 存,描述每个变量名、类型、是否必填、默认值。渲染时先查模板,再用变量值填充。

渲染引擎我推荐用 Handlebars 或 Mustache,语法简单,功能够用。不要用 FreeMarker 或 Velocity,太重了,而且语法复杂容易写出难以维护的模板。渲染时要做好变量校验,必填变量缺失直接报错,不要渲染出半成品提示词发给模型。

模板的灰度发布通过版本号加路由规则实现。请求里带上租户 ID 或用户 ID,底座根据配置决定用哪个版本。灰度比例可以按百分比,也可以按白名单。我建议灰度初期用白名单,找几个内部用户先试,稳定后再逐步放量。

4.5 会话状态的读写与过期策略

会话状态用 Redis Hash 存储,key 是会话 ID,field 是轮次序号,value 是消息内容。这样存储的好处是可以按轮次读取,不用一次性加载全部历史。读取最近 N 轮时,用HSCAN或按序号范围查,效率比读整个 String 高。

过期策略分两层:Redis 层面设置 TTL,比如 2 小时;数据库层面设置归档策略,比如 7 天后清理。TTL 到期后,热数据消失,但归档数据还在,需要时可以回源。归档可以用定时任务批量写入,不要每次写会话都同步写数据库,那样数据库压力太大。

并发写的问题用 Redis 的WATCH或 Redisson 的分布式锁解决。我倾向于用 Redisson,API 更友好,而且有看门狗机制自动续期,不用担心锁过期。但要注意锁的粒度,按会话 ID 加锁,不要全局一把锁。

4.6 限流熔断规则的配置与验证

限流用 Sentinel 或 Resilience4j 都行。Sentinel 的规则可以动态推送到 Nacos,改规则不用重启。配置限流规则时,除了 QPS,还要加并发线程数限流。AI 调用的并发线程数建议设小一点,比如单实例 50,因为每个线程占用的资源多。

熔断规则要区分异常类型。模型超时算慢调用,触发熔断;模型返回 4xx 错误算业务异常,不触发熔断;模型返回 5xx 算系统异常,触发熔断。Sentinel 支持按异常类型配置,但需要自定义BlockExceptionHandler来区分。

验证限流熔断是否生效,可以用 JMeter 或 wrk 压测。压测时观察日志里有没有触发限流,熔断后有没有走降级逻辑。我建议在测试环境专门做一轮故障注入,手动把模型服务停掉,看底座是否能正确降级,降级后的响应时间是否在可接受范围内。

5. 踩坑实录:那些文档里不会写的经验

5.1 虚拟线程与 ThreadLocal 的冲突

JDK 21 的虚拟线程对 ThreadLocal 的支持有变化。传统平台线程里,ThreadLocal 是每个线程一份,用起来很顺手。虚拟线程数量巨大,如果每个虚拟线程都持有一份 ThreadLocal 副本,内存会爆。所以虚拟线程默认不支持 ThreadLocal,需要用ScopedValue替代。

QuickBlue 里如果用了 ThreadLocal 存用户上下文、租户信息,迁移到虚拟线程时会出问题。我踩过的坑是:在虚拟线程里取 ThreadLocal 值,取到的是 null 或者别的请求的值。解决办法是把这些上下文改成方法参数传递,或者用ScopedValue。改造量不小,但必须做,否则线上会出现串数据的严重 bug。

注意:如果暂时改不动,可以在虚拟线程的入口处显式拷贝 ThreadLocal 值到局部变量,但这是权宜之计,长期还是要用 ScopedValue。

5.2 流式响应的背压处理

流式输出时,模型吐 token 的速度可能快于客户端消费的速度。如果没有背压机制,数据会在内存里堆积,最终 OOM。Spring WebFlux 的Flux天然支持背压,但如果你用的是 MVC 加SseEmitter,就要自己处理。

我的做法是在SseEmitter的onCompletion和onTimeout回调里做清理,同时限制每个连接的缓冲区大小。当缓冲区满时,暂停从模型读取,等客户端消费后再继续。这需要和模型适配器配合,适配器要支持暂停和恢复读取。实现起来有点复杂,但流式场景下这是必须的。

5.3 模型返回内容的安全过滤

模型返回的内容可能包含不当信息,底座需要在返回给用户前做过滤。过滤可以在两个地方做:模型适配器里做一次,网关里再做一次。适配器里做是为了尽早拦截,网关里做是为了兜底。

过滤规则要可配置,不同业务场景的严格程度不一样。内部工具可以宽松一些,面向公众的产品要严格。过滤命中后,是直接替换敏感词,还是返回兜底话术,也要可配置。我建议至少支持替换和拦截两种模式。

5.4 常见问题速查表

问题现象可能原因排查方向解决办法
启动报类冲突Spring Cloud 与 Alibaba 版本不匹配检查版本对应表按官方对应关系调整版本
流式输出中断网关超时时间太短查看网关超时配置调大网关和负载均衡的超时
会话串数据ThreadLocal 在虚拟线程中失效检查上下文传递方式改用 ScopedValue 或方法参数
限流不生效Sentinel 规则未推送成功查看 Nacos 配置和 Sentinel 日志检查规则格式和推送通道
模型调用超时超时设置过短或网络问题查看模型响应时间和网络延迟按模型维度调整超时,加网络监控
提示词渲染报错变量缺失或类型不匹配查看渲染日志和模板定义补全变量,加渲染前校验
Redis 内存增长快会话数据未设 TTL检查 Redis key 的过期时间设置合理 TTL,加归档任务
熔断后不恢复熔断时间窗口设置过长查看熔断器配置调整时间窗口和半开探测策略

5.5 性能调优的几个关键参数

虚拟线程的调度器默认用 ForkJoinPool,并行度默认是 CPU 核数。IO 密集型场景下,这个并行度可能不够,可以适当调大。但不要调得太大,否则上下文切换开销会抵消收益。我一般设成 CPU 核数的 2 到 4 倍,具体看压测结果。

Redis 连接池的大小也要调。会话读写频繁,连接池太小会成为瓶颈。建议最大连接数设成 50 到 100,具体看并发量。但要注意 Redis 服务端的连接数限制,别把 Redis 打挂了。

模型调用的 HTTP 客户端连接池同样重要。如果用 OkHttp,默认连接池是 5 个,肯定不够。调到 50 到 100,并且开启连接复用。如果用 WebClient,底层是 Reactor Netty,连接池配置在HttpClient里。

6. 底座之上还能长什么:扩展方向与个人体会

QuickBlue 作为底座,本身不产生业务价值,价值在于让上层业务跑得更快更稳。底座稳定之后,可以在上面长出的东西很多。比如统一的 AI 效果评估模块,自动收集用户反馈,计算模型回答的满意度,为提示词优化提供数据支撑。再比如成本核算模块,按租户、按业务线统计 token 消耗,让 AI 成本可分摊、可控制。

还有一个方向是智能路由。现在模型路由大多是静态配置,指定哪个业务用哪个模型。将来可以根据请求内容自动选择模型:简单问题走小模型,复杂问题走大模型;中文问题走中文优化模型,代码问题走代码模型。这需要底座积累足够的调用数据,训练一个路由决策模型。听起来有点绕,但逻辑上说得通。

我在实际搭建类似底座的过程中,最大的体会是:不要一开始就追求大而全。先把模型接入和会话管理这两个最核心的模块做扎实,让业务能跑起来。限流熔断、提示词管理、可观测性这些可以逐步加。我见过一个团队,底座还没跑通就设计了十几个模块,结果三个月没上线,业务方失去耐心,项目被砍。底座的价值在于被使用,不被使用的底座设计得再漂亮也是零。

另一个体会是,底座的接口要尽量稳定,内部实现可以频繁改。业务方依赖的是接口,接口一变他们就要跟着改,怨声载道。所以设计接口时要多想几步,留好扩展点。比如模型调用的返回结果,除了内容本身,还要预留 token 消耗、模型标识、延迟等字段,将来要用的时候不用改接口。

最后分享一个小技巧:底座上线初期,一定要加一个“旁路模式”。业务请求正常走原有逻辑,同时异步复制一份到新底座,对比两边的输出和延迟。确认底座稳定后,再逐步切流量。这样即使底座出问题,业务也不受影响。旁路模式跑一两周,心里就有底了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询