跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaAIjava

Spring Boot 实战:基于 WebClient 的 SSE 流式接口

Spring Boot 项目中接入 AI 服务常遇同步阻塞问题。通过对比 Feign 与 WebClient 特性,采用 SseEmitter 结合 WebClient 构建 SSE 流式接口,解决长连接与流式消费难题。引入 Spring Cloud LoadBalancer 处理服务发现,避免 UnknownHostException。最终实现前后端实时交互,提升用户体验。

日志猎手发布于 2026/4/7更新于 2026/7/730 浏览

从 Feign 到 WebClient 的一次真实踩坑记录

一、为什么要把同步改成流式?

在最近的一个项目中,我负责接入一个 AI 问答服务。一开始的接口形态非常常规:客户端发请求,服务端等 AI 全部生成完内容,再一次性返回。

问题很快就暴露了:

  • AI 返回慢(10 秒甚至更久)
  • 用户页面'卡死',体验极差
  • 其实 AI 是'边生成边返回'的,但我们完全浪费了这个能力

于是,目标就很明确了:把原有同步接口,改造成支持 SSE(Server-Sent Events)的流式接口。

二、什么是 SSE?为什么适合 AI 场景?

SSE(Server-Sent Events)是一种 服务器主动推送 的 HTTP 通信方式。它基于 HTTP,单向(服务端 → 客户端),保持长连接,传输文本流(text/event-stream)。

返回的数据长这样:

data: 你好
data: 我是
data: AI

客户端可以一边接收,一边渲染。

对比一下其他技术:

技术适配度
HTTP 普通接口❌ 等全部生成
WebSocket❌ 太重
SSE✅ 天生流式

AI 的输出特征是 token 级 / 句子级生成,可边生成边消费,用户随时可能中断。SSE 几乎是最优解。

三、第一个坑:Feign 不支持 SSE

项目里原本调用 AI 服务用的是 Feign:

@FeignClient("mb-ai")
RespBean sendQuery(...)

一开始我尝试'硬改',但很快发现:Feign 本质是一次性 HTTP 调用,它不支持流式消费响应体。

哪怕 AI 服务是 SSE,Feign 也会等完整响应,再反序列化,流式直接失效。

结论很明确:

❌ Feign 不能用于 SSE ✅ SSE 必须用 WebClient / HttpClient

四、正确姿势:WebClient + SseEmitter

1. Controller 层:返回 SseEmitter

SSE 接口和普通接口最大的不同是:返回值不再是业务对象,而是一个'连接本身'。

@PostMapping(
    value = "/health_manager/stream",
    produces = MediaType.TEXT_EVENT_STREAM_VALUE
)
public SseEmitter healthManagerStream(@RequestBody HealthManagerQueryDTO request) {
    SseEmitter emitter = new SseEmitter(0L); // 不超时
    aiService.streamQuery(request, emitter);
    return emitter;
}

关键点:

  • produces = text/event-stream
  • 返回 SseEmitter
  • 业务逻辑交给 Service
2. Service 层:WebClient 真正消费 AI 流
webClient.post()
    .uri("/health_manager")
    .contentType(MediaType.APPLICATION_JSON)
    .accept(MediaType.TEXT_EVENT_STREAM)
    .bodyValue(request)
    .retrieve()
    .bodyToFlux(String.class)
    .subscribe(
        data -> emitter.send(data),
        error -> emitter.completeWithError(error),
        emitter::complete
    );

这段代码的含义是:AI 每吐一段数据,我就 emitter.send(),前端立刻收到。真正实现了'边生成、边返回、边渲染'。

五、第二个大坑:UnknownHostException: mb-ai

代码写完,一跑,直接报错:

java.net.UnknownHostException: mb-ai

第一反应:'不对啊,Feign 一直是能调用 mb-ai 的'。

原因分析
  • Feign:自动走注册中心(Nacos / Eureka)
  • WebClient:只认 DNS
.baseUrl("http://mb-ai")

在 WebClient 看来,mb-ai 就是一个普通域名,但 DNS 根本不认识它。

六、正确解法:WebClient 接入服务发现

1. 引入 LoadBalancer
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2. 给 WebClient.Builder 加 @LoadBalanced
@Configuration
public class WebClientConfig {
    @Bean
    @LoadBalanced
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}
3. baseUrl 继续用服务名
.baseUrl("http://mb-ai")

此时调用链变成:WebClient → LoadBalancer → Nacos → 真实 IP:PORT。UnknownHostException 到此彻底解决。

七、最终依赖组合(最小可用)

<!-- WebClient / SSE -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 服务发现 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Nacos(项目里一般已有) -->
spring-cloud-starter-alibaba-nacos-discovery

⚠️ 不会把项目变成 WebFlux,只是'在 MVC 项目里用 WebClient'。

八、架构上的最终形态

现在的做法是混合使用:

  • Feign └── 普通同步接口(兼容老系统)
  • WebClient └── SSE 流式接口(AI 问答)

接口层设计成:

  • POST /health_manager // 非流式
  • POST /health_manager/stream // SSE

前端可以按需选择。

九、一些实战踩坑总结

  • ❌ Feign 强行做 SSE → 行不通
  • ❌ WebClient 不加 LoadBalanced → 必炸 UnknownHostException
  • ❌ 忘了 produces → 前端收不到流
  • ❌ AI 实际没返回 text/event-stream → 你这边再对也没用

十、写在最后

这次改造最大的收获不是'把 SSE 跑通了',而是更清楚地理解了:

  • Feign 和 WebClient 的边界
  • 同步接口和流式接口在架构层面的本质差异
  • AI 场景对交互模型的倒逼

如果你现在也在做 AI 问答、长文本生成或实时推送,那么,SSE 几乎是绕不开的一步。

目录

  1. 从 Feign 到 WebClient 的一次真实踩坑记录
  2. 一、为什么要把同步改成流式?
  3. 二、什么是 SSE?为什么适合 AI 场景?
  4. 三、第一个坑:Feign 不支持 SSE
  5. 四、正确姿势:WebClient + SseEmitter
  6. 1. Controller 层:返回 SseEmitter
  7. 2. Service 层:WebClient 真正消费 AI 流
  8. 五、第二个大坑:UnknownHostException: mb-ai
  9. 原因分析
  10. 六、正确解法:WebClient 接入服务发现
  11. 1. 引入 LoadBalancer
  12. 2. 给 WebClient.Builder 加 @LoadBalanced
  13. 3. baseUrl 继续用服务名
  14. 七、最终依赖组合(最小可用)
  15. 八、架构上的最终形态
  16. 九、一些实战踩坑总结
  17. 十、写在最后
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Linux 下 libwebkit2gtk-4.1-0 安装与配置指南
  • C++ 函数重载:核心规则、实现原理与实战
  • 风险管控而非修补:金仓 SQL 防火墙体系化实践
  • DeerFlow 2.0 深度解析:字节跳动开源的超级 AI 智能体框架
  • OpenClaw 安装部署指南 - 搭建自托管 AI 助手
  • AI 核心概念速通教程:人工智能基础入门
  • C++11 核心特性详解:列表初始化、右值引用与移动语义
  • OpenClaw 汉化中文版部署指南:npm、Docker 安装与环境配置
  • 全网降AIGC工具大盘点:DeepSeek、笔灵AI等评测
  • 2026 届学位论文 AIGC 检测率要求汇总及应对策略
  • HDFS 分布式文件系统数据读写流程解析
  • Linux 终端动态进度条实现:原理、编码与优化
  • 2026 年国家自然科学基金申请书 AI 使用声明撰写位置指南
  • AI 剧透功能创意:初级开发者的反压制生存指南
  • 网络安全入门常见误区与学习路径指南
  • OpenClaw 多 Agent 协作:串联计算与翻译任务
  • Java SE 多线程并发:锁策略、JUC 与原理
  • 频率自适应多模态推荐的结构化谱推理
  • Android View 点击与触摸事件优先级分析
  • 基于协同过滤算法的理财产品推荐系统 Flask 实现

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online