Spring Boot 之 AsyncRestTemplate 連接池配置

AsyncRestTemplate 連接池配置

通常業務中的 HTTP 請求都是同步調用的。如果請求響應比較慢,甚至請求超時,程序就必須等到請求返回以後才能繼續執行,在某些場合下,我並不需要等待請求的結果,或者我不關心請求是否執行成功,需要繼續執行之後的邏輯,就需要通過異步處理。

AsyncRestTemplate 是實現異步調用的工具。

AsyncRestTemplate 的連接池實現如下

配置文件類

    /**
     * RestTemplate 客戶端 Httpclient 的線程池配置
     *
     * @author fengxuechao
     * @version 0.1
     * @date 2019/11/12
     */
    @ConfigurationProperties(prefix = "http-pool")
    @Data
    public class HttpPoolProperties {
    
        /**
         * 最大線程數
         */
        private Integer maxTotal = 20;
    
        /**
         * 默認線程數
         */
        private Integer defaultMaxPerRoute = 10;
    
        /**
         * 連接上服務器(握手成功)的時間
         */
        private Integer connectTimeout = 1000;
    
        /**
         * 從連接池中獲取連接的超時時間
         */
        private Integer connectionRequestTimeout = 3000;
    
        /**
         * 服務器返回數據(response)的時間
         */
        private Integer socketTimeout = 5000;
    
        /**
         * 用於校驗線程空閒的時間
         */
        private Integer validateAfterInactivity = 7000;
    
        /**
         * 開啓異步線程池
         */
        private Boolean async = false;
    
    }

連接池配置類

忽略SSL

    /**
     * @author fengxuechao
     * @version 0.1
     * @date 2019/12/17
     */
    @Slf4j
    @Component
    public class HttpClientHelper {
    
        public SSLContext getSslContext() {
            // 在調用SSL之前需要重寫驗證方法,取消檢測SSL
            X509TrustManager trustManager = new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
    
                @Override
                public void checkClientTrusted(X509Certificate[] xcs, String str) {
                }
    
                @Override
                public void checkServerTrusted(X509Certificate[] xcs, String str) {
                }
            };
            SSLContext ctx = null;
            try {
                ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
                ctx.init(null, new TrustManager[]{trustManager}, null);
            } catch (NoSuchAlgorithmException | KeyManagementException e) {
                log.error("創建 SSL 失敗", e);
            }
            assert ctx != null;
            return ctx;
        }
    
    }

配置類

    /**
     * @author fengxuechao
     * @version 0.1
     * @date 2019/12/17
     */
    @Slf4j
    @Configuration
    @ConditionalOnClass({HttpAsyncClient.class})
    @ConditionalOnProperty(prefix = "http-pool", name = "async", havingValue = "true")
    @EnableConfigurationProperties(HttpPoolProperties.class)
    public class HttpAsyncClientConfig {
    
        @Autowired
        private HttpPoolProperties httpPoolProperties;
    
        @Autowired
        private HttpClientHelper helper;
    
        /**
         * 異步 Http 連接池
         *
         * @return
         */
        @Bean
        @ConditionalOnMissingBean(AsyncClientHttpRequestFactory.class)
        public AsyncClientHttpRequestFactory asyncClientHttpRequestFactory() {
            HttpComponentsAsyncClientHttpRequestFactory factory = new HttpComponentsAsyncClientHttpRequestFactory(httpAsyncClient());
            factory.setConnectTimeout(httpPoolProperties.getConnectTimeout());
            factory.setReadTimeout(httpPoolProperties.getSocketTimeout());
            factory.setConnectionRequestTimeout(httpPoolProperties.getConnectionRequestTimeout());
            return factory;
        }
    
        /**
         * 異步 Http 客戶端
         *
         * @return
         */
        @Bean("httpAsyncClient")
        @ConditionalOnMissingBean(HttpAsyncClient.class)
        public HttpAsyncClient httpAsyncClient() {
            SSLContext sslContext = helper.getSslContext();
            Registry<SchemeIOSessionStrategy> registry = RegistryBuilder.<SchemeIOSessionStrategy>create()
                    .register("http", NoopIOSessionStrategy.INSTANCE)
                    .register("https", new SSLIOSessionStrategy(sslContext))
                    .build();
            ConnectingIOReactor ioReactor = null;
            try {
                ioReactor = new DefaultConnectingIOReactor();
            } catch (IOReactorException e) {
                log.error("構建異步連接失敗", e);
            }
            assert ioReactor != null;
            PoolingNHttpClientConnectionManager connectionManager = new PoolingNHttpClientConnectionManager(ioReactor, registry);
            connectionManager.setMaxTotal(httpPoolProperties.getMaxTotal());
            connectionManager.setDefaultMaxPerRoute(httpPoolProperties.getDefaultMaxPerRoute());
            RequestConfig requestConfig = RequestConfig.custom()
                    //服務器返回數據(response)的時間,超過拋出read timeout
                    .setSocketTimeout(httpPoolProperties.getSocketTimeout())
                    //連接上服務器(握手成功)的時間,超出拋出connect timeout
                    .setConnectTimeout(httpPoolProperties.getConnectTimeout())
                    //從連接池中獲取連接的超時時間,超時間未拿到可用連接,會拋出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
                    .setConnectionRequestTimeout(httpPoolProperties.getConnectionRequestTimeout())
                    .build();
            return HttpAsyncClientBuilder.create()
                    .setDefaultRequestConfig(requestConfig)
                    .setConnectionManager(connectionManager)
                    .build();
        }
    }

AsyncRestTemplate 配置

    /**
     * @author fengxuechao
     * @version 0.1
     * @date 2019/11/12
     */
    @Configuration
    @Import({HttpClientConfig.class})
    public class RestTemplateAutoConfiguration {
    
        /**
         * 異步 RestTemplate
         *
         * @return
         */
        @Bean("asyncRestTemplate")
        @ConditionalOnMissingBean
        @ConditionalOnBean(value = {AsyncClientHttpRequestFactory.class, ClientHttpRequestFactory.class})
        public AsyncRestTemplate asyncRestTemplate(
                AsyncClientHttpRequestFactory asyncClientHttpRequestFactory,
                ClientHttpRequestFactory httpRequestFactory) {
            RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
            restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
            return new AsyncRestTemplate(asyncClientHttpRequestFactory, restTemplate);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章