restTemplate设置httpClient线程池

Posted 好大的月亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了restTemplate设置httpClient线程池相关的知识,希望对你有一定的参考价值。

第一步肯定是先引入依赖

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.7</version>
</dependency>

java连接池配置类

常量类

package com.fchen.usercenter.config;


import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Data
public class HttpPoolConstants {

    @Value("${httpPool.restTemplateCharset:utf-8}")
    private String restTemplateCharset;

    @Value("${httpPool.poolMaxSize:500}")
    private Integer poolMaxSize;

    @Value("${httpPool.maxPerRoute:50}")
    private Integer maxPerRoute;

    @Value("${httpPool.requestConfig.socketTimeout:10000}")
    private Integer socketTimeout;

    @Value("${httpPool.requestConfig.connectTimeout:5000}")
    private Integer connectTimeout;

    @Value("${httpPool.requestConfig.connectionRequestTimeout:1000}")
    private Integer connectionRequestTimeout;

    @Value("${httpPool.maxIdleTime:5000}")
    private Integer maxIdleTime;

    @Value("${httpPool.defaultKeepAliveTime:10000}")
    private Integer defaultKeepAliveTime;

    @Value("${httpPool.specialKeepAliveTimeHostName:''}")
    private String specialKeepAliveTimeHostName;

}

连接池配置类,主要是制造出连接池的ClientHttpRequestFactory

package com.fchen.usercenter.config;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import java.util.concurrent.TimeUnit;


@Configuration
@Slf4j
public class HttpClientPoolConfig {

    @Autowired
    private HttpPoolConstants httpPoolConstants;

    private ObjectMapper objectMapper;

    {
        objectMapper = new ObjectMapper();
    }



    @Bean("restHttpRequestFactory")
    public ClientHttpRequestFactory httpRequestFactory() {
        return new HttpComponentsClientHttpRequestFactory(httpClient());
    }

    @Bean("restHttpClient")
    public HttpClient httpClient() {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        //设置整个连接池最大连接数 根据自己的场景决定
        connectionManager.setMaxTotal(httpPoolConstants.getPoolMaxSize());

        //路由是对maxTotal的细分,针对一个url的最大并发数,每路由最大连接数,默认值是2
        connectionManager.setDefaultMaxPerRoute(httpPoolConstants.getMaxPerRoute());
		//针对所有的请求设置超时时间,也可以放到后面针对某一个url设置这些个超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(httpPoolConstants.getSocketTimeout()) //服务器返回数据(response)的时间,超过该时间抛出read timeout
                .setConnectTimeout(httpPoolConstants.getConnectTimeout())//连接上服务器(握手成功)的时间,超出该时间抛出connect timeout
                .setConnectionRequestTimeout(httpPoolConstants.getConnectionRequestTimeout())//从连接池中获取连接的超时时间,超过该时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
                .build();


         /*
            启动清理线程,也可以启用用CloseableHttpClient的清理线程
            Thread t = new IdleConnectionMonitorThread(connectionManager);
            t.setName("httpconnections-pool-evict-thread");
            t.start();
         */

        return HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                //设置后台线程剔除失效连接
                .evictExpiredConnections().evictIdleConnections(httpPoolConstants.getMaxIdleTime(), TimeUnit.SECONDS)
                .build();
    }
}

RestTemplate注入到spring上下文的时候设置ClientHttpRequestFactory

@Bean
public RestTemplate restTemplate(HttpClientPoolConfig httpClientPoolConfig){
    return new RestTemplate(httpClientPoolConfig.httpRequestFactory());
}

或者直接用HttpClient直接跑post请求

这里只是截取了一段,作为demo展示如何使用HttpClient直接执行post请求,但是这里要注意HttpClient要保证是单例的,不要每次都new一个HttpClient

URI uri = new URIBuilder(xxx).setPath(xxx).setCharset(Consts.UTF_8).build();

fileName = (null == fileName || fileName.isEmpty()) ? generateFileName() : fileName;

HttpPost httpPost = new HttpPost(uri);
httpPost.addHeader("content-type", "text/plain");
httpPost.addHeader("xxx", xxx);
httpPost.addHeader("filename", fileName);

//针对这个url的post请求设置各种超时时间
this.setRequestConfig(httpPost);

InputStreamEntity inputStreamEntity = new InputStreamEntity(inputStream);
inputStreamEntity.setContentEncoding(charsetUtf8);
httpPost.setEntity(inputStreamEntity);
// 发起请求 并返回请求的响应
CloseableHttpResponse response = httpClient.execute(httpPost);

int statusCode = response.getStatusLine().getStatusCode();

if (statusCode >= 200 && statusCode < 300) {
    if (null == response.getEntity()) {
        // 说明返回结果不正确
        throw new RuntimeException(fileName + " request failure:Response is empty");
    }

    String entity = EntityUtils.toString(response.getEntity(), Consts.UTF_8);

    Result result = JSON.parseObject(entity, Result.class);

    if (result.getCode() == 1 && null != result.getData() && !result.getData().isEmpty()) {
        return fileName;
    } else {
        throw new RuntimeException(fileName + " upload failure:" + result.getCode() + "," + result.getMsg());
    }
} else {
    throw new RuntimeException("request failure:" + statusCode + EntityUtils.toString(response.getEntity(), Consts.UTF_8));
}


单独针对某一个url请求设置各种超时时间

private void setRequestConfig(HttpPost httpPost) {
    RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(this.socketTimeout).setSocketTimeout(this.socketTimeout).setConnectionRequestTimeout(this.connectionRequestTimeout).build();
    httpPost.setConfig(requestConfig);
}

以上是关于restTemplate设置httpClient线程池的主要内容,如果未能解决你的问题,请参考以下文章

记一次解决RestTemplate和HttpClient请求结果乱码的问题

使用当前 httpclient (4.x) 的 RestTemplate 基本或摘要身份验证

Httpclient与RestTemplate的比较(比httpClient更优雅的Restful URL访问)

RestTemplate

RestTemplate

JavaSpring之RestTemplate的使用