【异常】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;
}
相关推荐
堕2742 分钟前
java数据结构当中的《排序》(一 )
java·数据结构·排序算法
亓才孓16 分钟前
[Class的应用]获取类的信息
java·开发语言
开开心心就好24 分钟前
AI人声伴奏分离工具,离线提取伴奏K歌用
java·linux·开发语言·网络·人工智能·电脑·blender
80530单词突击赢37 分钟前
JavaWeb进阶:SpringBoot核心与Bean管理
java·spring boot·后端
爬山算法1 小时前
Hibernate(87)如何在安全测试中使用Hibernate?
java·后端·hibernate
云姜.1 小时前
线程和进程的关系
java·linux·jvm
是码龙不是码农1 小时前
支付防重复下单|5 种幂等性设计方案(从初级到架构级)
java·架构·幂等性
曹牧1 小时前
Spring Boot:如何在Java Controller中处理POST请求?
java·开发语言
heartbeat..1 小时前
JVM 性能调优流程实战:从开发规范到生产应急排查
java·运维·jvm·性能优化·设计规范
WeiXiao_Hyy1 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端