原文链接:SpringAI(1.1.0-M)—MCP Server各协议分析及示例
[!TIP] Streamable HTTP 更适合大规模企业级部署
实战代码可见:github.com/GTyingzi/sp… 下的 mcp 目录下的
- server/mcp-streamable-webmvc-server、server/mcp-streamable-webflux-server
- client/mcp-streamable-client、client/mcp-streamable-webflux-client
各协议详细分析
STDIO:本地进程通信的简单方案
描述:STDIO 协议是 Spring AI MCP Server 最基础的传输实现,它基于标准输入/输出流进行通信,无需网络协议支持。在技术实现上,客户端直接启动并管理服务器进程,通过向服务器标准输入(stdin)写入消息,从标准输出(stdout)读取消息完成交互
优势:
- 简单和安全性:数据传输完全在进程内存中进行,避免了网络传输的安全风险
- 无需网络配置:开发者只需通过命令行启动服务进程即可开始通信
- 低延迟低场景:适合需要快速响应的本地工具调用
劣势:
- 仅支持本地通信:无法跨网络或分布式环境使用
- 并发处理限制:采用同步机制,单线程处理请求,难以应对高并发场景
- 资源管理问题:每次请求都需要重建连接,无法有效复用资源
SSE:传统 HTTP 流式传输的单向方案
描述:SSE 协议是 Spring AI 早期版本中主流的远程传输方案,基于 HTML5 标准的服务器发送事件技术。在 Spring AI 框架中,SSE 分为两种实现方式:WebMVC 模式(基于 Servlet API)和 WebFlux 模式(基于响应式编程)
特点:服务器向客户端单向推送数据,允许服务器在建立连接后随时发送实时更新,无需客户端反复发起请求
优势:
- 实时推送能力:支持长连接保持,适合需要持续更新的场景
- 实现复杂度低:客户端只需通过浏览器原生支持的 EventSource 对象即可实现连接
- 传统环境集成方便:适合与现有 Spring MVC 项目无缝衔接
劣势:
- 高并发资源消耗:每个连接需占用约 80KB 内存,万级并发时可能导致服务器资源耗尽
- 连接稳定性差:在弱网环境下中断率高达 15%-30%,且不支持断线自动恢复
- 架构扩展性限制:强制要求服务器维护粘性会话,在负载均衡场景下增加了配置复杂度
Streamable HTTP:平衡性能与状态的创新方案
描述 Streamable HTTP 协议是 MCP 协议在 2025 年 3 月的重大升级,它取代了原有的 HTTP+SSE 作为默认传输方式。Streamable HTTP 的核心创新在于统一了请求/响应端点,支持按需流式传输和会话管理,同时保留了 HTTP 协议的简洁性
特点:在高并发场景下,TCP 连接数仅为几十条,远低于 SSE 的上千条,显著降低了服务器资源压力。响应时间方面,Streamable HTTP 在 1000 并发用户测试中平均响应时间为 7.5ms,而 SSE 飙升至 1511ms,性能提升近 200 倍
Stateless Streamable HTTP:无状态设计的极致优化
描述:Stateless Streamable HTTP 是 Streamable HTTP 的无状态变体,它通过移除会话状态管理,进一步优化了资源利用率和扩展性。Stateless 模式的核心理念是将状态管理责任从服务器转移到客户端,每次请求都包含完整的上下文信息
资源效率优势
- 内存消耗降至 5KB/请求以下,且在空闲状态下资源占用趋近于零
- 水平扩展能力强:请求可在服务器集群中任意路由,无需复杂的粘性会话机制
- 网络兼容性好:完全遵循标准 HTTP 语义,能更好地穿透企业防火墙
实现复杂度
- 客户端实现复杂:会话状态完全由客户端管理,增加了客户端实现复杂度
- 复杂交互处理困难:对于需要持续会话的复杂交互,可能导致客户端代码臃肿
- 断线重连要求高:客户端需主动传递所有必要信息,增加了实现难度
总结
下面是 Higrees 官网的测试文章:(使用 Streamable HTTP 前后的数据比对)
在 1000 个并发用户的测试场景下,TCP 连接数的变化
- **HTTP + SSE:**需要维持大量长连接,TCP 连接数随时间持续增长
- **Streamable HTTP:**按需建立连接,TCP 连接数维持在较低水平
模拟不同并发用户数下的请求成功率测试
- **HTTP + SSE:**随着并发用户数增加,成功率显著下降
- **Streamable HTTP:**即使在高并发场景下仍能保持较高的请求成功率
不同用户数量下的平均返回响应时间
- HTTP + SSE 的平均响应时间更长,在高并发场景下响应时间波动较大
- Streamable HTTP 的平均响应时间更短,响应时间波动较小,随并发用户数增加,响应时间增长更平
适用场景推荐
| 推荐协议 | 推荐理由 | |
| 本地开发与调试场景 | STDIO | 无需网络配置,实现简单,适合快速验证工具逻辑和本地调试。例如,开发一个天气查询工具时,可以通过STDIO协议快速启动服务进程并测试工具功能 |
| 传统Servlet应用的实时通知 | WebMVC模式的SSE | 与现有Spring MVC项目无缝集成,适合简单的实时通知场景,如聊天室消息推送。但需注意高并发场景下的性能问题 |
| 高性能流式交互场景 | Streamable HTTP | 在高并发场景下性能优势明显,同时支持会话状态管理和断线重连。例如,实现一个AI对话系统,用户发送查询后,系统以流形式返回处理结果 |
| 大规模分布式系统 | Stateless Streamable HTTP | 无状态设计使请求可在集群中自由路由,支持真正的线性扩展。例如,微服务架构中的工具调用服务,每个请求都是独立的 |
下面带来 Streamable-HTTP 的 WebFlux 的示例
server 侧
pom 依赖
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 20000
spring:
application:
name: mcp-streamable-webflux-server
ai:
mcp:
server:
name: streamable-mcp-server
protocol: streamable # SSE、STREAMABLE、STATELESS
version: 1.0.0
type: ASYNC # Recommended for reactive applications
instructions: "This reactive server provides time information tools and resources"
capabilities:
tool: true
resource: true
prompt: true
completion: true
request-timeout: 20s
streamable-http:
mcp-endpoint: /mcp
keep-alive-interval: 30s
disallow-delete: false
# 调试日志
logging:
level:
io:
modelcontextprotocol:
client: DEBUG
spec: DEBUG
server: DEBUG
-
spring.ai.mcp.server.protocol:选择协议,这里可以选三种类型SSE、STREAMABLE、STATELESS
-
spring.ai.mcp.server.streamable-http:
- mcp-endpoint:设置端点,默认为/mcp
- keep-alive-interval:定期向所有活跃的会话发送心跳消息,以维持活跃状态,默认不设置
- disallow-delete:是否允许客户端通过 DELETE 请求来删除会话
工具及启动类
package com.spring.ai.tutorial.mcp.server.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author yingzi
* @date 2025/5/28 08:55
*/
@Service
public class TimeService {
private static final Logger logger = LoggerFactory.getLogger(TimeService.class);
@Tool(description = "Get the time of a specified city.")
public String getCityTimeMethod(@ToolParam(description = "Time zone id, such as Asia/Shanghai") String timeZoneId) {
logger.info("The current time zone is {}", timeZoneId);
return String.format("The current time zone is %s and the current time is " + "%s", timeZoneId,
getTimeByZoneId(timeZoneId));
}
private String getTimeByZoneId(String zoneId) {
// Get the time zone using ZoneId
ZoneId zid = ZoneId.of(zoneId);
// Get the current time in this time zone
ZonedDateTime zonedDateTime = ZonedDateTime.now(zid);
// Defining a formatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
// Format ZonedDateTime as a string
String formattedDateTime = zonedDateTime.format(formatter);
return formattedDateTime;
}
}
package com.spring.ai.tutorial.mcp.server;
import com.spring.ai.tutorial.mcp.server.service.TimeService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
/**
* @author yingzi
* @since 2025/10/3
*/
@SpringBootApplication
public class StreamableWebfluxServerApplication {
public static void main(String[] args) {
SpringApplication.run(StreamableWebfluxServerApplication.class, args);
}
@Bean
public ToolCallbackProvider timeTools(TimeService timeService) {
return MethodToolCallbackProvider.builder().toolObjects(timeService).build();
}
}
client 侧
pom 依赖
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 19100
spring:
application:
name: mcp-streamable-webflux-client
main:
web-application-type: none
ai:
openai:
api-key: ${DASHSCOPEAPIKEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode
chat:
options:
model: qwen-max
mcp:
client:
enabled: true
name: my-mcp-client
version: 1.0.0
request-timeout: 600s
type: ASYNC # or ASYNC for reactive applications
streamable-http:
connections:
server1:
url: http://localhost:20000
endpoint: /mcp
启动类
package com.spring.ai.tutorial.mcp.client;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Scanner;
/**
* @author yingzi
* @since 2025/10/3
*/
@SpringBootApplication
public class StreamableWebfluxClientApplication {
public static void main(String[] args) {
SpringApplication.run(StreamableWebfluxClientApplication.class, args);
}
@Bean
public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools,
ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultToolCallbacks(tools.getToolCallbacks())
.build();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("\n>>> QUESTION: ");
String userInput = scanner.nextLine();
if (userInput.equalsIgnoreCase("exit")) {
break;
}
System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput).call().content());
}
scanner.close();
context.close();
};
}
}
效果
往期资料
Spring AI + Spring Ai Aliabba系统化学习资料
本教程将采用2025年5月20日正式的GA版,给出如下内容
- 核心功能模块的快速上手教程
- 核心功能模块的源码级解读
- Spring ai alibaba增强的快速上手教程 + 源码级解读
版本:
- JDK21
- SpringBoot3.4.5
- SpringAI 1.0.3
- SpringAI Alibaba 1.0.0.4
免费渠道:
- 为Spring Ai Alibaba开源社区解决解决有效的issue or 提供有价值的PR,可免费获取上述教程
- 往届微信推文
收费服务:收费69.9元
- 飞书在线云文档
- Spring AI会员群教程代码答疑
- 若Spring AI、Spring AI Alibaba教程内容无法满足业务诉求,可定制提供解决方案,带价私聊
学习交流圈
你好,我是影子,曾先后在🐻、新能源、老铁就职,兼任Spring AI Alibaba开源社区的Committer。目前新建了一个交流群,一个人走得快,一群人走得远,另外,本人长期维护一套飞书云文档笔记,涵盖后端、大数据系统化的面试资料,可私信免费获取