【异常】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;
}
相关推荐
EatFan8 小时前
Spring Boot 4 落地观察:从 yudao-cloud、matecloud、JPower 看国产脚手架的升级路线与迁移清单
java·spring boot·后端·spring cloud·微服务·后端开发·jdk 21
Maiko Star9 小时前
* LangChain 提示词模板详解:ChatPromptTemplate 的使用与高级特性
java·人工智能·langchain
波力海苔夹心脆67510 小时前
C# 序列化与反序列化详解:System.Text.Json、Newtonsoft.Json、XmlSerializer 用法、特性选项与安全实践
经验分享·后端·c#·json·.net
现任明教教主~11 小时前
Thinkphp站群蜘蛛池SaaS系统YanyvSEO含多用户/积分/六大引擎计费/易支付对接
java·开发语言·spring
JavaGuide11 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel11 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
一条破秋裤11 小时前
Linux 线程创建:pthread_create 与基本回收
java·linux·运维
布吉岛的石头12 小时前
Java 程序员第 49 阶段5:BERT 预训练目标 MLM+NSP 的工程含义
java·人工智能·深度学习·bert·transformer
摇滚侠12 小时前
《On Java 中文版 基础卷》阅读笔记 对象无处不在 03
java·笔记·python