一文打盡Spring Cloud線程和超時配置

本文基於Spring Boot 2.6.6和Spring Cloud 2021.0.1

一、Spring Cloud Gateway

1.1 client部分

gateway向其他服務發起請求所用的配置,使用的是reactor.netty.http.client.HttpClient。

相關配置都在org.springframework.cloud.gateway.config.HttpClientProperties類中

# 連接超時時間(毫秒)
spring.cloud.gateway.httpclient.connect-timeout=45000
# 請求響應等待時間
spring.cloud.gateway.httpclient.response-timeout=null

# 線程池類型,除了默認的elastic,還支持fixed和disabled
# elastic:無限擴展的線程池
# fixed:固定數量線程池
# disabled:不使用線程池——只有一個線程
spring.cloud.gateway.httpclient.pool.type=elastic
# 最大線程數,只在類型爲fixed時有效
spring.cloud.gateway.httpclient.pool.max-connections=CPU核數*2(最小爲16)
# 從線程池獲取連接的最大等待時間(毫秒)
spring.cloud.gateway.httpclient.pool.acquire-timeout=45000
# 線程空閒多久會被回收(毫秒)
spring.cloud.gateway.httpclient.pool.max-idle-time=null
# 線程最大存活時間(毫秒)
spring.cloud.gateway.httpclient.pool.max-life-time=null
# 定時檢測週期(毫秒),檢測哪些線程需要被回收
# (1)只有空閒的線程會被回收
# (2)即使未達到max-idle-time,當達到max-life-time時也會被回收
spring.cloud.gateway.httpclient.pool.eviction-interval=null

1.2 server部分

gateway接收請求的部分(請求一般來自於前端),使用的是reactor.netty.http.server.HttpServer。

對應配置org.springframework.boot.autoconfigure.web.ServerProperties#Netty

# 連接超時時間(毫秒)
server.netty.connection-timeout=30000
# 連接等待時間(毫秒),超時會被自動關閉。爲空表示永遠不關閉,全靠請求方
server.netty.idle-timeout=null

注意,spring boot沒有把netty-web-server的線程參數配置開放出來,個人猜測是因爲netty調高線程數其實並沒有什麼作用,如果需要修改的話可以通過以下兩種方式:

(1)自行聲明ReactorResourceFactory

@Bean
public ReactorResourceFactory reactorServerResourceFactory() {
    ReactorResourceFactory reactorResourceFactory = new ReactorResourceFactory();
    reactorResourceFactory.setUseGlobalResources(false);
    // 設置event loop線程池
    reactorResourceFactory.setLoopResources(LoopResources.create("my-http1", 16, true));
    // 設置連接池
    reactorResourceFactory.setConnectionProvider(ConnectionProvider.create("my-http2", 16));
    return reactorResourceFactory;
}

(2)通過設置系統參數或者環境變量的方式

詳見reactor.netty.resources.ConnectionProvider和reactor.netty.resources.LoopResources類

# 設置最大連接數,最小爲16
-Dreactor.netty.pool.maxConnections=xxx
# 設置IO線程數,最小爲4
-Dreactor.netty.ioWorkerCount=xxx
# 設置selector線程數,最小爲4
-Dreactor.netty.ioSelectCount=xxx

二、Spring WebFlux

spring cloud gateway server部分本質上就是一個webflux應用,所以相關配置可以直接參考gateway的。

當然有個不同的地方就是WebFlux是可以使用tomcat的,不過既然選擇了使用WebFlux的編程模型,一般都會使用netty或者undertow這些,這裏就不擴展了。

三、Spring MVC(Tomcat)

# 最大線程數
server.tomcat.threads.max=200
# 最小線程數
# tomcat沒有暴露idletime的配置,默認就是60s
# 詳情查看org.apache.tomcat.util.net.AbstractEndpoint#createExecutor方法
server.tomcat.threads.min-spare=10
# 對應JDK nio-server的backlog參數,可以簡單認爲是等待隊列的大小
server.tomcat.accept-count=100
# 最大連接數,因爲是NIO模型,所以這個值遠遠大於線程數
server.tomcat.max-connections=8192
# org.apache.coyote.Processor的緩存大小,被緩存的processor會複用之前的request和response對象
server.tomcat.processor-cache=200
# 對應socket的SO_TIMEOUT(毫秒),其實就是連接等待時間
server.tomcat.connection-timeout=20000
# 指的是HTTP中的keep-alive功能(毫秒),在一定時間內http請求會複用tcp連接
server.tomcat.keep-alive-timeout=20000
# 允許keep-alive的最大請求數
server.tomcat.max-keep-alive-requests=200

四、Spring Cloud LoadBalancer

Spring Cloud在2020版本的時候廢棄了Netflix-ribbon,改爲了自研的Spring Cloud LoadBalancer。

LoadBalancer本身只負責多個服務之間負載均衡的處理,具體的請求是通過對接各種第三方客戶端實現的,比如Apache HttpComponents、Okhttp、Reactor Netty等等,具體的配置跟使用LoadBalancer的模塊有關(參考下邊幾個小節)。

五、Feign With LoadBalancer

底層基於Apache HttpComponents 4.x(可以自行切換成其他HTTP框架)。

相關配置見org.springframework.cloud.openfeign.support.FeignHttpClientProperties和org.springframework.cloud.openfeign.FeignClientProperties類

# 建立連接的超時時間(毫秒)
feign.httpclient.connection-timeout=2000
# 定時檢測任務週期(毫秒),用來定期清理連接池中過期的連接
feign.httpclient.connection-timer-repeat=3000
# 連接池最大數量
feign.httpclient.max-connections=200
# 單個url的最大連接數量
feign.httpclient.max-connections-per-route=50
# 連接的存活時間(秒)
feign.httpclient.time-to-live=900

# 全局連接超時時間(毫秒)
feign.client.config.default.connect-timeout=10000
# 全局讀寫超時時間(毫秒)
feign.client.config.default.read-timeout=60000

六、RestTemplate With LoadBalancer

底層基於Apache HttpComponents 4.x(可以自行切換成其他HTTP框架)。

spring自帶的org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration配置類是無法對各種超時時間進行配置的,可以通過以下方法修改。

注意:雖然創建CloseableHttpClient 的時候有一個連接超時時間,但是在配置Feign和RestTemplate的時候這個時間會被覆蓋掉

@Configuration(proxyBeanMethods = false)
static class RestTemplateConfiguration {

    private final Timer connectionManagerTimer = new Timer("HttpClientConfiguration.connectionManagerTimer", true);

    private CloseableHttpClient httpClient;

    @Autowired(required = false)
    private RegistryBuilder registryBuilder;

    @Bean
    public CloseableHttpClient httpClient(FeignHttpClientProperties httpClientConfig,
                                          ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
                                          ApacheHttpClientFactory httpClientFactory) {

        HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(
                httpClientConfig.isDisableSslValidation(), httpClientConfig.getMaxConnections(),
                httpClientConfig.getMaxConnectionsPerRoute(), httpClientConfig.getTimeToLive(),
                httpClientConfig.getTimeToLiveUnit(), registryBuilder
        );
        // 取代HttpComponents自帶的過期檢查,因爲配置不夠靈活,詳見IdleConnectionEvictor
        this.connectionManagerTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                connectionManager.closeExpiredConnections();
            }
        }, 30000, httpClientConfig.getConnectionTimerRepeat());

        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(httpClientConfig.getConnectionTimeout())
                .setRedirectsEnabled(httpClientConfig.isFollowRedirects())
                .build();
        this.httpClient = httpClientFactory.createBuilder()
                .setDefaultRequestConfig(defaultRequestConfig)
                .setConnectionManager(connectionManager)
                .build();
        return this.httpClient;
    }

    @PreDestroy
    public void destroy() {
        this.connectionManagerTimer.cancel();
        if (this.httpClient != null) {
            try {
                this.httpClient.close();
            } catch (IOException e) {
                log.error("Could not correctly close httpClient.");
            }
        }
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        requestFactory.setConnectTimeout(5 * 1000);
        requestFactory.setConnectionRequestTimeout(60 * 1000);
        requestFactory.setReadTimeout(30 * 60 * 1000);
        return requestFactory;
    }

    @Bean
    public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer,
                                                   ClientHttpRequestFactory clientHttpRequestFactory) {
        RestTemplateBuilder builder = new RestTemplateBuilder();
        builder = builder.requestFactory(() -> clientHttpRequestFactory);
        return restTemplateBuilderConfigurer.configure(builder);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}

七、WebClient With LoadBalancer

參考gateway的server部分自行聲明ReactorResourceFactory,不過要保證你的聲明早於org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorConfiguration類

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章