【异常】Spring Ai Alibaba 流式输出卡住无响应的问题

Spring Ai Alibaba 流式输出卡住无响应的问题

关键点

RestClientCustomizer

WebClientCustomizer 重点 流式输出使用这个

java 复制代码
// 定义全局WebClient
    @Bean
    public WebClientCustomizer webClientCustomizer() {


        ConnectionProvider providerWeb = ConnectionProvider.builder("webClient-pool")
                // 1️⃣ 最大连接数:≈ 并发 + 冗余
                .maxConnections(500)
                // 2️⃣ 等待连接的请求上限(防止雪崩)
                .pendingAcquireMaxCount(200)
                // 3️⃣ 等待连接的最长时间
                .pendingAcquireTimeout(Duration.ofSeconds(30))
                // 4️⃣ 空闲连接回收(SSE 结束后)
                .maxIdleTime(Duration.ofSeconds(30))
                // 5️⃣ 单连接最大存活时间(防"僵尸连接")
                .maxLifeTime(Duration.ofMinutes(7))
                // 6️⃣ 后台定期回收
                .evictInBackground(Duration.ofSeconds(30))
                // 7️⃣ 开启 metrics(强烈建议)
                .metrics(true)
                .build();



        HttpClient httpClientWeb = HttpClient.create(providerWeb)
                .protocol(HttpProtocol.HTTP11) //  HTTP/1.1
                // 连接超时:5秒
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000)
                // 响应超时:60秒  这里不用控制 通过ReadTimeout来控制
//           .responseTimeout(Duration.ofSeconds(60*10))
                .doOnConnected(conn ->
                        //读超时(比如每 60 秒没收到数据就断开
                        conn.addHandlerLast(new ReadTimeoutHandler(60))     // 可选:更细粒度读超时(需导入 netty-handler)
                )
//                .wiretap(true); // 开启日志(需配置 logging.level.reactor.netty=DEBUG)
                ;
        return builder -> builder.clientConnector(
                new ReactorClientHttpConnector(httpClientWeb)
        );
    }

com.alibaba.cloud.ai.dashscope.api.DashScopeAgentApi

//初始化 webClient

private final RestClient restClient;

private final WebClient webClient;

java 复制代码
  public DashScopeAgentApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder,
                             WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) {



       ConnectionProvider providerRest = ConnectionProvider.builder("dashscope-restClient-pool")
             // 1️⃣ 最大连接数:≈ 并发 + 冗余
             .maxConnections(500)
             // 2️⃣ 等待连接的请求上限(防止雪崩)
             .pendingAcquireMaxCount(200)
             // 3️⃣ 等待连接的最长时间
             .pendingAcquireTimeout(Duration.ofSeconds(30))
             // 4️⃣ 空闲连接回收(SSE 结束后)
             .maxIdleTime(Duration.ofSeconds(30))
             // 5️⃣ 单连接最大存活时间(防"僵尸连接")
             .maxLifeTime(Duration.ofMinutes(7))
             // 6️⃣ 后台定期回收
             .evictInBackground(Duration.ofSeconds(30))
             // 7️⃣ 开启 metrics(强烈建议)
             .metrics(true)
             .build();



       HttpClient httpClientRest = HttpClient.create(providerRest)
             .protocol(HttpProtocol.HTTP11) //  HTTP/1.1
             // 连接超时:5秒
             .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000)
             // 响应超时:60秒  这里不用控制 通过ReadTimeout来控制
//           .responseTimeout(Duration.ofSeconds(60*10))
             .doOnConnected(conn ->
                   //读超时(比如每 60 秒没收到数据就断开
                   conn.addHandlerLast(new ReadTimeoutHandler(60))     // 可选:更细粒度读超时(需导入 netty-handler)
             );
//           .wiretap(true); // 开启日志(需配置 logging.level.reactor.netty=DEBUG)


       this.restClient = restClientBuilder.baseUrl(baseUrl)
             .defaultHeaders(ApiUtils.getJsonContentHeaders(apiKey))
             .defaultStatusHandler(responseErrorHandler)
             .requestFactory(new ReactorClientHttpRequestFactory(httpClientRest))
             .build();



       ConnectionProvider providerWeb = ConnectionProvider.builder("dashscope-webClient-pool")
             // 1️⃣ 最大连接数:≈ 并发 + 冗余
             .maxConnections(500)
             // 2️⃣ 等待连接的请求上限(防止雪崩)
             .pendingAcquireMaxCount(200)
             // 3️⃣ 等待连接的最长时间
             .pendingAcquireTimeout(Duration.ofSeconds(30))
             // 4️⃣ 空闲连接回收(SSE 结束后)
             .maxIdleTime(Duration.ofSeconds(30))
             // 5️⃣ 单连接最大存活时间(防"僵尸连接")
             .maxLifeTime(Duration.ofMinutes(7))
             // 6️⃣ 后台定期回收
             .evictInBackground(Duration.ofSeconds(30))
             // 7️⃣ 开启 metrics(强烈建议)
             .metrics(true)
             .build();


       HttpClient httpClientWeb = HttpClient.create(providerWeb)
             .protocol(HttpProtocol.HTTP11) //  HTTP/1.1
             // 连接超时:5秒
             .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000)
             // 响应超时:60秒  这里不用控制 通过ReadTimeout来控制
//           .responseTimeout(Duration.ofSeconds(60*10))
             .doOnConnected(conn ->
                   //读超时(比如每 60 秒没收到数据就断开
                   conn.addHandlerLast(new ReadTimeoutHandler(60))     // 可选:更细粒度读超时(需导入 netty-handler)
             );
//           .wiretap(true); // 开启日志(需配置 logging.level.reactor.netty=DEBUG)



       this.webClient = webClientBuilder.baseUrl(baseUrl)
             .defaultHeaders(ApiUtils.getJsonContentHeaders(apiKey, null, true))
             .clientConnector(new ReactorClientHttpConnector(httpClientWeb))
             .build();
    }

单独定义

java 复制代码
@Bean
public RestClientCustomizer restClientCustomizer() {
    ClientHttpRequestFactory factory = clientHttpRequestFactory();
    return builder -> builder.requestFactory(factory);
}



/**
 * 配置ClientHttpRequestFactory
 *
 * @return ClientHttpRequestFactory实例
 */
private ClientHttpRequestFactory clientHttpRequestFactory() {

    // 1. 配置连接层(TCP 连接、SSL 握手等)
    ConnectionConfig connectionConfig = ConnectionConfig.custom()
            .setConnectTimeout(Timeout.ofSeconds(60)) // TCP + SSL 握手超时
            .build();


    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
    connectionManager.setMaxTotal(1024); // Set max total connections
    connectionManager.setDefaultMaxPerRoute(1024); // Set max connections per route
    connectionManager.setDefaultConnectionConfig(connectionConfig);

    // 显式定义超时
    RequestConfig requestConfig =
            RequestConfig.custom()
                    //大模型调用 需要预留充足时间
                    .setResponseTimeout(Timeout.ofSeconds(300)) // 注意:HttpClient 5 中是 responseTimeout(不是 socketTimeout)
                    .build();

    CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .setDefaultRequestConfig(requestConfig)
            .disableAutomaticRetries()
            .evictExpiredConnections()
            .evictIdleConnections(TimeValue.ofSeconds(60)) // 每60秒清理空闲连接
            .build();

    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
    clientHttpRequestFactory.setConnectTimeout(60_000);
    clientHttpRequestFactory.setConnectionRequestTimeout(60_000);
    clientHttpRequestFactory.setReadTimeout(Duration.ofSeconds(60));

    return clientHttpRequestFactory;
}
相关推荐
期待のcode3 小时前
Java虚拟机栈
java·开发语言·jvm
珂朵莉MM3 小时前
全球校园人工智能算法精英大赛-产业命题赛-算法巅峰赛 2025年度画像
java·人工智能·算法·机器人
芒克芒克3 小时前
本地部署SpringBoot项目
java·spring boot·spring
cute_ming3 小时前
关于基于nodeMap重构DOM的最佳实践
java·javascript·重构
sww_10263 小时前
Netty原理分析
java·网络
小突突突3 小时前
Spring框架中的单例bean是线程安全的吗?
java·后端·spring
iso少年3 小时前
Go 语言并发编程核心与用法
开发语言·后端·golang
掘金码甲哥3 小时前
云原生算力平台的架构解读
后端
码事漫谈3 小时前
智谱AI从清华实验室到“全球大模型第一股”的六年征程
后端
码事漫谈3 小时前
现代软件开发中常用架构的系统梳理与实践指南
后端