一、问题现象
我们使用 Spring Boot 2.7 + RestTemplate 调用下游 MCP 服务(HTTP + JSON)。压测时发现:

平均响应时间:320ms(正常应在 100ms 内)

P99 响应时间:2100ms

服务端 CPU 和内存均正常,无慢 SQL 或 GC 问题

调用方(Spring)线程大量处于 TIMED_WAITING 状态

初步判断:瓶颈不在服务端,而在客户端调用链路。

二、定位过程
1. 确认网络延迟
用 curl 直接调用 MCP 服务,耗时仅 45ms。说明网络和服务端处理没有问题,问题出在 Spring 调用封装层。

2. 检查 RestTemplate 配置
发现用的是默认的 RestTemplate,没有配置连接池和超时。默认使用 SimpleClientHttpRequestFactory,每个请求都会新建连接,且没有连接复用。

3. 抓包分析
用 Wireshark 看到大量 TCP 三次握手和四次挥手,每个请求都重新建立连接,TCP 握手耗时 + TLS 握手(如果是 HTTPS) 占了大头。

4. 日志分析
打印调用耗时,发现 RestTemplate.exchange() 之前有一段莫名其妙的延迟(~150ms),后来定位到是因为在过滤器里做了同步的用户信息解析,阻塞了 IO。

三、解决方案汇总
针对定位到的几个问题,我逐一做了优化:


下面给出核心代码实现。

四、核心优化代码
1. 配置带连接池的 RestTemplate
java
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        // 使用 HttpClient 连接池
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(200);           // 最大连接数
        connectionManager.setDefaultMaxPerRoute(50);  // 每个路由最大并发

        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(3000)               // 连接超时 3s
                .setSocketTimeout(5000)                // 读取超时 5s
                .setConnectionRequestTimeout(2000)     // 从连接池获取连接超时
                .build();

        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(requestConfig)
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE) // 保持长连接
                .build();

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(factory);
    }
}
2. 改用 WebClient 实现异步非阻塞调用
java
@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient(WebClient.Builder builder) {
        return builder
                .baseUrl("http://mcp-service")
                .defaultHeader("Content-Type", "application/json")
                .clientConnector(new ReactorClientHttpConnector(
                        HttpClient.create()
                                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                                .responseTimeout(Duration.ofSeconds(5))
                                .doOnConnected(conn -> 
                                    conn.addHandlerLast(new ReadTimeoutHandler(5))
                                        .addHandlerLast(new WriteTimeoutHandler(3)))
                                .compress(true)  // 开启压缩
                ))
                .build();
    }
}
调用示例:

java
@Service
public class McpService {

    private final WebClient webClient;

    public McpService(WebClient webClient) {
        this.webClient = webClient;
    }

    public Mono<McpResponse> callMcp(McpRequest request) {
        return webClient.post()
                .uri("/api/endpoint")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(McpResponse.class)
                .timeout(Duration.ofSeconds(5))
                .retryWhen(Retry.backoff(2, Duration.ofMillis(100))); // 重试策略
    }
}
在 Controller 中返回 Mono,充分利用 Spring WebFlux 的非阻塞特性。

3. 使用本地缓存减少重复调用
对于不经常变化的数据(如用户权限、配置信息),引入 Caffeine 缓存:

java
@Configuration
public class CacheConfig {

    @Bean
    public Cache<String, UserAuth> userAuthCache() {
        return Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .recordStats()
                .build();
    }
}
4. 开启 HTTP 压缩(服务端和客户端)
在 MCP 服务端(如 Tomcat)配置压缩:

properties
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml
server.compression.min-response-size=1024
客户端自动处理 Accept-Encoding: gzip,RestTemplate 和 WebClient 默认支持,无需额外配置。

五、优化效果
压测结果(100 并发,持续 5 分钟):

指标    优化前    优化后    提升
平均响应时间    320 ms    62 ms    81% ↓
P99 响应时间    2100 ms    145 ms    93% ↓
吞吐量 (TPS)    280    1520    5.4 倍 ↑
调用线程阻塞数    经常满 (200)    < 10    极大改善
同时,服务端 CPU 使用率也从 45% 下降到 22%,因为减少了大量无用的连接建立和销毁开销。

六、经验总结
永远不要用默认的 RestTemplate 去做生产环境的高频调用,连接池是标配。

超时必须设置,否则一个慢服务会拖垮整个调用链。

异步化是提升吞吐量的利器,Spring WebFlux + WebClient 非常适合 IO 密集型场景。

性能问题先从客户端找原因,不要一上来就怀疑服务端。

善用工具:Wireshark(网络)、Arthas(线程栈)、JMeter(压测)、SkyWalking(链路追踪)。

最后,如果你的 MCP 服务不是 HTTP 协议,而是基于 TCP 或 gRPC,优化思路类似:连接池、多路复用、异步、超时、压缩、缓存 六大法宝。

希望这篇文章能帮到你。如果你也有类似的踩坑经验,欢迎在评论区交流!

Spring AI 实战视频课程地址:https://edu.csdn.net/course/detail/41074

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐