☰
mcp-for-beginners 实战:用 Spring Boot WebFlux + SSE 构建 HTTP 流式计算服务(Calculator HTTP Streaming Demo 全解析)
2026/10/2 15:44:00 网站建设 项目流程
  • 教程
  • 文档
  • 人工智能

【免费下载链接】mcp-for-beginners

This open-source curriculum introduces the fundamentals of Model Context Protocol (MCP) through real-world, cross-language examples in .NET, Java, TypeScript, JavaScript, Rust and Python. Designed for developers, it focuses on practical techniques for building modular, scalable, and secure AI workflows from session setup to service orchestration.

项目地址:https://gitcode.com/GitHub_Trending/mc/mcp-for-beginners
点击查看免费下载

本文以本仓库03-GettingStarted/06-http-streaming章节的 Java 版 Calculator HTTP Streaming Demo 为骨架,结合仓库内完整源码,讲解如何用 Spring Boot WebFlux 与 Server-Sent Events(SSE)实现真正的"边算边推"流式 HTTP 服务:服务端逐条推送计算进度与结果事件,客户端实时消费并打印。读完本文,你将掌握 SSE 的传输格式、Reactive 流式 API(Flux<ServerSentEvent<T>>与WebClient.bodyToFlux())的完整用法,并能把这一模式迁移到 MCP(Model Context Protocol)流式通知的实战开发中。

背景:为什么需要 HTTP 流式传输

在正式进入 Java 代码之前,先厘清一个关键概念。流式传输(Streaming)是一种网络编程技术,它允许数据以小块或事件序列的方式逐步发送与接收,而不是等整个响应组装完成后再一次性返回。它在以下场景尤其重要:

  • 大文件、大数据集的分批传输;
  • 实时更新(聊天消息、进度条);
  • 长时间运行的计算任务,希望持续向用户反馈中间状态。

其核心价值是:数据渐进式到达、客户端边到边处理,从而降低感知延迟、改善用户体验。关于流式传输与 MCP 传输机制(stdio、HTTP+SSE、Streamable HTTP)的系统对比,可参考章节总文档 06-http-streaming/README.md,其中明确指出:HTTP+SSE 已在 MCP2025-03-26版本弃用,并由 Streamable HTTP 取代;本 Java 示例属于"经典 HTTP 流式传输"教学示例(SSE),用于演示流式编程范式本身,而 MCP 中的流式则表现为"进度/日志通知 + 最终结果单次返回"的结构化 JSON-RPC 消息模式。

项目结构

本示例由两个 Maven 工程组成,完整源码位于03-GettingStarted/06-http-streaming/solution/java/:

java/ ├── calculator-server/ # Spring Boot 服务端,暴露 SSE 端点 │ ├── src/main/java/com/example/calculatorserver/ │ │ ├── CalculatorServerApplication.java # 启动入口 │ │ └── CalculatorController.java # /calculate SSE 控制器 │ ├── src/main/resources/ │ │ └── application.yml # 端口配置(8080) │ └── pom.xml ├── calculator-client/ # Spring Boot 客户端应用 │ ├── src/main/java/com/example/calculatorclient/ │ │ └── CalculatorClientApplication.java # 流式消费端 │ └── pom.xml └── README.md # 本示例的原始说明文档

工作原理

  1. Calculator Server暴露/calculate端点:
    • 接收查询参数a(数值)、b(数值)、op(运算类型);
    • 支持add、sub、mul、div四种运算;
    • 以 Server-Sent Events 的形式返回计算进度与最终结果。
  2. Calculator Client连接服务端:
    • 发起一次7 * 5的计算请求;
    • 消费流式响应;
    • 将每个事件逐条打印到控制台。

源码级解析:服务端如何"边算边推"

服务端全部逻辑浓缩在一个控制器中,源码见 CalculatorController.java。

1. 声明 SSE 端点

@GetMapping(value = "/calculate", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<ServerSentEvent<String>> calculate(@RequestParam double a, @RequestParam double b, @RequestParam String op) {
  • produces = MediaType.TEXT_EVENT_STREAM_VALUE指定响应内容类型为text/event-stream,这是 SSE 的标准 MIME 类型,浏览器与各种 HTTP 客户端据此识别流式事件;
  • 方法返回Flux<ServerSentEvent<String>>——这是 Project Reactor 的响应式流类型,意味着响应体不是一次性字符串,而是一个可订阅的事件流,这正是 Spring WebFlux 支持流式响应的关键;
  • 三个@RequestParam分别绑定a、b、op,其中a、b为double,op为String。

2. 运算分发逻辑

double result; switch (op) { case "add": result = a + b; break; case "sub": result = a - b; break; case "mul": result = a * b; break; case "div": result = b != 0 ? a / b : Double.NaN; break; default: result = Double.NaN; }
  • add(加)、sub(减)、mul(乘)、div(除)四种运算;
  • 除法的边界处理值得注意:当b == 0时返回Double.NaN而不是抛出异常,避免请求直接 500;
  • 未匹配的op(如pow、sqrt)同样落入default分支返回Double.NaN——从源码结构看,目前没有对非法运算做显式错误事件,这是文档"Next Steps"建议改进的点之一。

3. 构造 SSE 事件流

return Flux.<ServerSentEvent<String>>just( ServerSentEvent.<String>builder() .event("info") .data("Calculating: " + a + " " + op + " " + b) .build(), ServerSentEvent.<String>builder() .event("result") .data(String.valueOf(result)) .build() ) .delayElements(Duration.ofSeconds(1));
  • Flux.just(...)一次性装配两个事件;ServerSentEvent.builder()为每个事件指定事件类型(event字段)与数据载荷(data字段);
  • 第一个事件类型为info,载荷是形如Calculating: 7.0 mul 5.0的进度描述;第二个事件类型为result,载荷是计算结果字符串;
  • .delayElements(Duration.ofSeconds(1))让两个事件之间间隔 1 秒发出,模拟真实场景中"处理需要耗时"的效果——这是让客户端真正体会到"流式到达"而不是"一次全给"的关键设计。

4. 服务端启动入口

CalculatorServerApplication.java 是标准的 Spring Boot 入口:

@SpringBootApplication public class CalculatorServerApplication { public static void main(String[] args) { SpringApplication.run(CalculatorServerApplication.class, args); } }

默认内嵌 Netty 非阻塞服务器,监听端口由 application.yml 指定为 8080:

server: port: 8080

源码级解析:客户端如何流式消费

客户端是一个实现了CommandLineRunner的 Spring Boot 应用,源码见 CalculatorClientApplication.java。

1. 构建 WebClient

private final WebClient client = WebClient.builder() .baseUrl("http://localhost:8080") .build();

WebClient是 Spring WebFlux 的响应式 HTTP 客户端,baseUrl指向服务端地址(默认端口 8080)。

2. 组装请求并流式接收

client.get() .uri(uriBuilder -> uriBuilder .path("/calculate") .queryParam("a", 7) .queryParam("b", 5) .queryParam("op", "mul") .build()) .accept(MediaType.TEXT_EVENT_STREAM) .retrieve() .bodyToFlux(String.class) .doOnNext(System.out::println) .blockLast();
  • uriBuilder.queryParam(...)依次注入a=7、b=5、op=mul,等价于请求GET /calculate?a=7&b=5&op=mul;
  • .accept(MediaType.TEXT_EVENT_STREAM)在请求头声明Accept: text/event-stream,告知服务端"我要 SSE 流";
  • .bodyToFlux(String.class)是关键一步:把响应体解码为一个Flux<String>,每个String对应 SSE 中的一个data:数据行——客户端从此获得一个可以逐条订阅的事件流;
  • .doOnNext(System.out::println)在每条数据到达时立即打印,实现"边到边打";
  • .blockLast()阻塞等待流结束(最后一条result到达后返回),保证CommandLineRunner在流消费完之前进程不退。

3. 启动入口

public static void main(String[] args) { SpringApplication.run(CalculatorClientApplication.class, args); }

客户端同样是一个标准 Spring Boot 应用,因为实现了CommandLineRunner,应用启动完成后会自动执行run(...)中的消费逻辑。

Maven 构建配置:两个工程的关键点

两个 pom.xml(客户端见 calculator-client/pom.xml)配置完全对称,值得注意的有三点:

  1. Java 版本与 Spring Boot 版本:
<properties> <java.version>17</java.version> <spring.boot.version>3.3.1</spring.boot.version> </properties>

通过dependencyManagement导入spring-boot-dependenciesBOM 统一管理依赖版本。

  1. 唯一的核心依赖:spring-boot-starter-webflux。服务端依赖它获得响应式 Web 框架(SSE 支持 + Netty),客户端依赖它获得WebClient。注释写得很直白:服务端<!-- Spring Boot WebFlux for SSE -->,客户端<!-- Spring Boot WebFlux for WebClient -->。

  2. -parameters编译标志(maven-compiler-plugin中<parameters>true</parameters>):该配置把方法参数名写入字节码,确保 Spring 能按名字绑定@RequestParam。若缺失此配置,可能出现参数绑定异常——这正是文档 Troubleshooting 第 3 条的来源,详见下文。

运行指南

方式一:使用 Maven(推荐)

1. 启动服务端
cd calculator-server mvn clean package mvn spring-boot:run

服务启动后监听http://localhost:8080,控制台输出类似:

Started CalculatorServerApplication in X.XXX seconds Netty started on port 8080 (http)
2. 运行客户端

另开一个终端,进入客户端目录:

cd calculator-client mvn clean package mvn spring-boot:run

客户端会自动连接服务端发起7 * 5的计算,并把流式事件逐条打印。

方式二:直接使用 Java 运行打包好的 Jar

1. 编译并运行服务端:
cd calculator-server mvn clean package java -jar target/calculator-server-0.0.1-SNAPSHOT.jar
2. 编译并运行客户端:
cd calculator-client mvn clean package java -jar target/calculator-client-0.0.1-SNAPSHOT.jar

手动测试服务端

除了运行配套客户端,还可以用浏览器或 curl 直接验证 SSE 端点。

使用浏览器

访问:

http://localhost:8080/calculate?a=10&b=5&op=add

浏览器会按 SSE 规范解析text/event-stream响应并持续显示到达的事件。

使用 curl

curl "http://localhost:8080/calculate?a=10&b=5&op=add" -H "Accept: text/event-stream"

通过-H "Accept: text/event-stream"显式声明接受 SSE 流,即可在终端逐条看到事件帧。

预期输出

运行客户端后,应看到类似以下的流式输出:

event:info data:Calculating: 7.0 mul 5.0 event:result data:35.0

两行之间空行分隔,正是 SSE 事件帧的标准格式:event:行声明事件类型,data:行承载载荷,空行表示一个事件结束。由于服务端delayElements设置了 1 秒间隔,你会先看到info事件,约 1 秒后再看到result事件——这就是"流式"与"一次性响应"最直观的体验差异。

API 参考

GET /calculate

请求参数:

参数必填类型说明
a是double第一个操作数
b是double第二个操作数
op是String运算类型:add、sub、mul、div

响应:

  • Content-Type: text/event-stream
  • 返回包含计算进度与结果的 Server-Sent Events 流

请求示例:

GET /calculate?a=7&b=5&op=mul HTTP/1.1 Host: localhost:8080 Accept: text/event-stream

响应示例:

event: info data: Calculating: 7.0 mul 5.0 event: result data: 35.0

支持的操作

操作含义结果
add加法a + b
sub减法a - b
mul乘法a * b
div除法b != 0 ? a / b : Double.NaN(除零返回 NaN)

故障排查

常见问题

  1. 端口 8080 被占用

    • 停止占用 8080 端口的其他应用;
    • 或在 calculator-server 的 application.yml 中修改server.port,并将客户端的baseUrl同步改为新端口。
  2. 连接被拒绝(Connection refused)

    • 确保先启动服务端再启动客户端;
    • 确认服务端已在 8080 端口成功启动(观察 "Netty started on port 8080" 日志)。
  3. 参数名绑定问题

    • 本项目在 Maven 编译插件中配置了-parameters标志(<parameters>true</parameters>);
    • 若遇到参数绑定异常,请确认项目是使用该配置构建的——缺少此标志时,Spring 无法按名称解析@RequestParam。

停止应用

  • 在每个应用运行的终端按Ctrl+C;
  • 或若以后台进程方式运行,使用mvn spring-boot:stop。

技术栈一览

组件说明
Spring Boot 3.3.1应用框架(BOM 统一管理版本)
Spring WebFlux响应式 Web 框架,提供 SSE 与 WebClient
Project Reactor响应式流库(Flux/ServerSentEvent)
Netty非阻塞 I/O 服务器(内嵌于 WebFlux)
Maven构建工具(含-parameters编译标志)
Java 17+编程语言与运行时

与 MCP 流式传输的衔接与扩展建议

本示例是理解 MCP 流式通知的绝佳铺垫。对照 06-http-streaming/README.md 中的对比表:

  • 经典 HTTP 流式(本示例):主响应本身就是分块流式传输,进度以数据块形式内嵌在主响应流中;
  • MCP 流式(Notifications):主结果仍是一次性返回,进度/日志以独立的 JSON-RPC 通知消息(LoggingMessageNotification)在过程中实时推送,客户端需实现消息处理器来区分"通知"与"最终结果"。

二者的设计哲学差异在于:经典流式把"进度"塞进响应体,MCP 把"进度"提升为结构化的一等消息类型。本仓库在同一章节提供了 MCP 流式的多语言实现(Python 版解决方案 使用ctx.info()发送进度通知并运行transport="streamable-http"),可与本 Java 示例对照学习。另外需注意:本仓库的教学示例明确标注了协议版本差异——章节文档提示,MCP 规范2025-11-25中的initialize握手、Mcp-Session-Id、GET 事件流等能力已被2026-07-28规范移除,后者改为自包含的 POST 请求,详见 01-CoreConcepts/mcp-2026-07-28.md,在新建实现前务必先行确认。

官方给出的下一步练习建议:

  • 增加更多数学运算(如pow、sqrt);
  • 为非法运算补充显式错误处理(当前default分支仅返回 NaN);
  • 添加请求/响应日志;
  • 实现认证机制;
  • 补充单元测试。

对 Java 开发者而言,建议直接修改CalculatorController的switch分支与Flux组装逻辑,观察 SSE 事件流的变化,从而深入理解响应式流式编程的每一环。

  • 教程
  • 文档
  • 人工智能

【免费下载链接】mcp-for-beginners

This open-source curriculum introduces the fundamentals of Model Context Protocol (MCP) through real-world, cross-language examples in .NET, Java, TypeScript, JavaScript, Rust and Python. Designed for developers, it focuses on practical techniques for building modular, scalable, and secure AI workflows from session setup to service orchestration.

项目地址:https://gitcode.com/GitHub_Trending/mc/mcp-for-beginners
点击查看免费下载
上一篇:PowerToys中文汉化终极指南:3分钟让微软工具箱变母语界面
下一篇:Puter.js 实战指南:零配置接入云存储、NoSQL、托管与 500+ AI 模型的单脚本开源方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询