You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot RestClient的spring.http.client.read-timeout属性为何上限仅30秒,设置更高值会触发空闲超时?

Spring Boot RestClient的spring.http.client.read-timeout属性为何上限仅30秒,设置更高值会触发空闲超时?

这个问题其实和Spring Boot默认使用的Jetty HTTP客户端的双重超时机制直接相关,咱们一步步拆解清楚:

  • Jetty的两个独立超时配置是核心原因
    Jetty HTTP客户端默认维护着两个互不干扰的超时规则:

    1. 总请求超时(Total Request Timeout):控制从请求发起开始,到完整接收完响应的总时长,这正是spring.http.client.read-timeout属性对应的配置项。
    2. 空闲超时(Idle Timeout):控制连接在没有任何数据传输(不管是请求体发送还是响应体接收)的情况下,允许保持空闲的最长时间。这个值Jetty默认是30秒,而且Spring Boot并没有提供对应的全局配置属性来直接修改它。
  • 两种异常场景的触发逻辑

    1. 当你把spring.http.client.read-timeout设为30s时:总请求超时和Jetty默认空闲超时都是30s,此时服务器延迟50秒响应,总请求超时会先触发,所以你看到的是「Total timeout 30000 ms elapsed」的异常。
    2. 当你把总超时设为60s时:总请求超时是60s,但空闲超时还是默认的30s。你的stub服务器延迟50秒才返回数据,意味着请求发出去后,连接在30秒内完全没有数据传输,这时候Jetty的空闲超时就会先触发,抛出「Idle timeout expired」的异常,直接“截胡”了你的总超时设置。
  • 为什么编程创建SimpleClientHttpRequestFactory就没问题?
    因为SimpleClientHttpRequestFactory默认使用的是JDK自带的HttpURLConnection,它的超时模型更简单——只有连接超时和读取超时(对应总请求时长),没有单独的空闲超时配置。所以你设置的读取超时会直接作为总请求时长生效,不会有额外的空闲超时干扰。

  • 如果想通过配置解决,该怎么做?
    你可以通过编程方式定制Jetty客户端,让空闲超时和总请求超时保持一致,避免被截胡。示例代码如下:

    @Bean
    public RestClient restClient(RestClient.Builder builder, 
                                 @Value("${spring.http.client.read-timeout}") Duration readTimeout) {
        JettyClientHttpRequestFactory jettyFactory = new JettyClientHttpRequestFactory();
        jettyFactory.setHttpClient(customJettyHttpClient(readTimeout));
        return builder.requestFactory(jettyFactory).build();
    }
    
    private HttpClient customJettyHttpClient(Duration totalTimeout) {
        HttpClient httpClient = new HttpClient();
        // 同步设置总请求超时和空闲超时,避免冲突
        httpClient.setRequestTimeout(totalTimeout);
        httpClient.setIdleTimeout(totalTimeout);
        return httpClient;
    }
    

内容来源于stack exchange

火山引擎 最新活动