Spring  RestTemplate配置 apache HttpClientPool(http数据连接池)

Spring RestTemplate配置 apache HttpClientPool(http数据连接池)

Scroll Down

介绍

RestTemplate

是Spring用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp。

Spring RestTemplate是Spring 提供的用于访问 Rest 服务的客端, RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率,所以很多客户端比如Android或者第三方服务商都是使用RestTemplate 请求 restful服务

Apache HttpClient 简介

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。

HttpClient 相比传统 JDK 自带的 URLConnection,增加了易用性和灵活性,它不仅是客户端发送 HTTP 请求变得容易,而且也方便了开发人员测试接口(基于 HTTP 协议的),即提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握 HttpClient 是很重要的必修内容,掌握 HttpClient 后,相信对于 HTTP 协议的了解会更加深入。

RestTemplate+HttpClient Pool 整合

1.pom引入

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.7</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <version>${spring.boot.version}</version>
</dependency>

2. Apache Client 配置

HttpClientProperties

package cn.hb0730.http.client.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * <p>
 * </P>
 *
 * @author bing_huang
 * @since V1.0
 */
@ConfigurationProperties(prefix = "spring.httpclient")
public class HttpClientProperties {
    /**
     * <p>
     *  连接池最大连接数
     * </p>
     */
    private Integer  maxTotal = 1000;
    /**
     * 每个主机的并发
     */
    private Integer maxPerRoute=100;

    /**
     * 链接超时
     */
    private Integer connectTimeout=2 * 1000;
    /**
     * 服务器返回数据(response)的时间
     */
    private Integer socketTimeout=2 * 1000;
    /**
     * 从连接池中获取连接的超时时间,超时间未拿到可用连接
     */
    private Integer connectionRequestTimeout=200;
    /**
     * 重试次数
     */
    private Integer retryHandler=3;
    public Integer getMaxTotal() {
        return maxTotal;
    }

    public void setMaxTotal(Integer maxTotal) {
        this.maxTotal = maxTotal;
    }

    public Integer getMaxPerRoute() {
        return maxPerRoute;
    }

    public void setMaxPerRoute(Integer maxPerRoute) {
        this.maxPerRoute = maxPerRoute;
    }

    public Integer getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(Integer connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public Integer getSocketTimeout() {
        return socketTimeout;
    }

    public void setSocketTimeout(Integer socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    public Integer getConnectionRequestTimeout() {
        return connectionRequestTimeout;
    }

    public void setConnectionRequestTimeout(Integer connectionRequestTimeout) {
        this.connectionRequestTimeout = connectionRequestTimeout;
    }

    public Integer getRetryHandler() {
        return retryHandler;
    }

    public void setRetryHandler(Integer retryHandler) {
        this.retryHandler = retryHandler;
    }
}

HttpClientPoolAutoConfig

package cn.hb0730.http.client.config;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
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.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;

/**
 * <p>
 * HttpClientPool
 * </P>
 *
 * @author bing_huang
 * @since V1.0
 */
@Configuration
@ConditionalOnClass({HttpClient.class})
@EnableConfigurationProperties(value = HttpClientProperties.class)
public class HttpClientPoolAutoConfig {
    private Logger logger = LoggerFactory.getLogger(HttpClientPoolAutoConfig.class);
    private final HttpClientProperties properties;

    public HttpClientPoolAutoConfig(HttpClientProperties properties) {
        this.properties = properties;
    }

    /**
     * <p>
     * create pool manager
     * </p>
     *
     * @return HttpClient PoolManager
     */
    @Bean
    public HttpClientConnectionManager poolConnectionManager() {
        ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
        LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainSocketFactory)
                .register("https", sslSocketFactory).build();
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry);
        manager.setMaxTotal(properties.getMaxTotal());
        manager.setDefaultMaxPerRoute(properties.getMaxPerRoute());
        return manager;
    }


    /**
     * httpClientBuilder
     *
     * @return httpClientBuilder
     */
    @Bean
    public HttpClientBuilder clientBuilder() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(properties.getConnectTimeout())
                .setSocketTimeout(properties.getSocketTimeout())
                .setConnectionRequestTimeout(properties.getConnectionRequestTimeout()).build();
        HttpClientBuilder clientBuilder = HttpClients.custom().setConnectionManager(poolConnectionManager());
        clientBuilder.setDefaultRequestConfig(requestConfig);
        clientBuilder.setRetryHandler(createRetryHandler());
        return clientBuilder;
    }

    private HttpRequestRetryHandler createRetryHandler() {
        //请求失败时,进行请求重试
        return new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
                logger.error("request error, message:{}", e.getMessage());
                if (i > properties.getRetryHandler()) {
                    //重试超过5次,放弃请求
                    logger.error("retry has more than 5 time, give up request");
                    return false;
                }
                if (e instanceof NoHttpResponseException) {
                    //服务器没有响应,可能是服务器断开了连接,应该重试
                    logger.error("receive no response from server, retry");
                    return true;
                }
                if (e instanceof SSLHandshakeException) {
                    // SSL握手异常
                    logger.error("SSL hand shake exception");
                    return false;
                }
                if (e instanceof InterruptedIOException) {
                    //超时
                    logger.error("InterruptedIOException");
                    return false;
                }
                if (e instanceof UnknownHostException) {
                    // 服务器不可达
                    logger.error("server host unknown");
                    return false;
                }
                if (e instanceof ConnectTimeoutException) {
                    // 连接超时
                    logger.error("Connection Time out");
                    return false;
                }
                if (e instanceof SSLException) {
                    logger.error("SSLException");
                    return false;
                }

                HttpClientContext context = HttpClientContext.adapt(httpContext);
                HttpRequest request = context.getRequest();
                if (!(request instanceof HttpEntityEnclosingRequest)) {
                    //如果请求不是关闭连接的请求
                    return true;
                }
                return false;
            }
        };
    }
}

3. 自定义restTemplate

package cn.hb0730.http.client.config;

import org.apache.http.impl.client.HttpClientBuilder;
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 org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * <p>
 * spring  ResTemplate + http Client Pool
 * </P>
 *
 * @author bing_huang
 * @since V1.0
 */
@Configuration
public class RestTemplateConfig {

    @Resource
    private HttpClientPoolAutoConfig poolAutoConfig;

    @Bean(name = "httpClientRestTemplate")
    public RestTemplate restTemplate(ClientHttpRequestFactory requestFactory) {
        return new RestTemplate(requestFactory());
    }

    /**
     * get requestFactory
     *
     * @return ClientHttpRequestFactory
     */
    @Bean
    public ClientHttpRequestFactory requestFactory() {
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        HttpClientBuilder httpClientBuilder = poolAutoConfig.clientBuilder();
        requestFactory.setHttpClient(httpClientBuilder.build());
        return requestFactory;
    }
}

4.测试

/**
 * <p>
 * </P>
 *
 * @author bing_huang
 * @since V1.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@ActiveProfiles
public class RestTemplateConfigTest {

    @Resource
    private RestTemplate httpClientRestTemplate;

    @Test
    public void test(){
        ResponseEntity<String> forEntity = httpClientRestTemplate.getForEntity("http://localhost:8089//system/parameterInfoBusiness/get_all_para", String.class);

    }
}

项目地址

githubhttp-restemplate-spring-boot为项目案例,spring-boot-test为该项目的测试案例