JAVA开发中的工具类——基于HttpClinet的RestTemplate

RestTemplate是Spring提供的用于发送HTTP请求的客户端工具,RestTmplate提供了很多便捷的方法,可以大大提供开发效率。RestTemplate默认依赖JDK的Http连接工具HttpUrlConnection,你也可以替换不同的源,比如OkHttp、Apache HttpComponents 等等。本文以HttpClient为例来实现RestTemplate工具类,重点关注以下几点:

1、连接池超线程数量

为避免每次请求都需要创建线程,造成不必要的系统开销,在Http请求时,可使用线程池提高线程的使用率

2、连接池线程超时设置

线程超时主要为了保证在数据请求过程中当依赖服务出现问题时能够快速中断请求,防止因线程阻塞造成雪崩效应。线程超时主要有发起连接超时及接收数据返回超时(对应着TCP连接的三次握手事件超时和数据传输时间超时)。

3、重试设置

在分布式环境下,网络不稳定的情况总是会存在的,因此为了减少因网络不稳定导致的请求失败,有必要在发起HTTP请求时进行有限次数的请求重试。此处强调的是有限次数的重试,主要是防止网络确实出现了问题时,不断重试导致系统资源耗尽等问题。

4、通用转发设置

我们在进行HTTP转发请求时,往往会有一些通用请求设置,此时可以在工具类中进行统一设置,提高代码的可维护性。

5、返回值

在Rest风格的HTTP请求模式下,可以基于String类型来接收接口返回值(String模式比较通用,即使是对象也可以通过转换成字符类型进行数据接收)。而在一些特殊场景我们可能也需要传递一些文件流,这种情况下,直接使用String类型来接收返回值则不能满足需求。因此,如果我们从通用性考虑的话,可以基于字节流的方式来接收返回数据。例如可以使用org.springframework.core.io.Resource.class类型来接收返回数据。

下面是具体的代码参考:

  • Mavan依赖
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
  • 工具类配置代码
import org.apache.http.Header;
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.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
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.BasicHeader;
import org.apache.http.protocol.HttpContext;
import org.springframework.beans.factory.annotation.Value;
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.net.ssl.SSLException;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

/**
 * RestTemplate配置类
 */
@Configuration
public class RestTemplateConfig {
    // 以下配置可根据项目实际情况将配置项配置在工程本地或者分布式配置中心
    @Value("${httpclient.connection-request-timeout}")
    private Integer connectionRequestTimeout;
    @Value("${httpclient.connect-timeout}")
    private Integer connectTimeout;
    @Value("${httpclient.socket-timeout}")
    private Integer socketTimeout;

    /**
     * 创建RestTemplate Bean
     * @return
     */
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate(httpRequestFactory());
//        restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(Charset.forName("UTF-8")));
        // 添加内容转换器(根据实际需要配置转化)
//        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
//        messageConverters.add(new StringHttpMessageConverter());
//        messageConverters.add(new FormHttpMessageConverter());
//        messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
//        messageConverters.add(new MappingJackson2HttpMessageConverter());
        return restTemplate;
    }

    /**
     * 通过HttpClient创建Http请求工厂
     * @return
     */
    @Bean
    public ClientHttpRequestFactory httpRequestFactory() {
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
        // 缓冲请求数据,默认值是true。通过POST或者PUT大量发送数据时,建议将此属性更改为false,以免耗尽内存。
        clientHttpRequestFactory.setBufferRequestBody(false);
        return clientHttpRequestFactory;
    }

    /**
     * 创建 HttpClient Bean
     * @return
     */
    @Bean
    public HttpClient httpClient() {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        // 1、连接管理
        // 设置整个连接池最大连接数 根据自己的场景决定
        connectionManager.setMaxTotal(200);
        // 路由是对maxTotal的细分
        connectionManager.setDefaultMaxPerRoute(100);
        // 2、请求相关配置
        RequestConfig requestConfig = RequestConfig.custom()
                // 从连接池中获取连接的超时时间,超过该时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
                .setConnectionRequestTimeout(connectionRequestTimeout)
                // 连接上服务器(握手成功)的时间,超出该时间抛出connect timeout
                .setConnectTimeout(connectTimeout)
                // 服务器返回数据(response)的时间,超过该时间抛出read timeout
                .setSocketTimeout(socketTimeout)
                .build();

        // 3、重试机制
        HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException e, int retryTimes, HttpContext httpContext) {
                // 重试次数最多3次
                if(retryTimes > 3){
                    return false;
                }
                // 请求发生一些IO类型的异常时,进行重试
                if (e instanceof UnknownHostException || e instanceof ConnectTimeoutException
                        || !(e instanceof SSLException) || e instanceof NoHttpResponseException) {
                    return true;
                }

                return false;
            }
        };

        // 4、设置默认的Header
        List<Header> headers = new ArrayList<>();
        headers.add(new BasicHeader("Accept-Encoding","gzip,deflate"));
        headers.add(new BasicHeader("Connection", "Keep-Alive"));

        // 创建httpclient对象
        return HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                .setRetryHandler(httpRequestRetryHandler)
                .setDefaultHeaders(headers)
                .build();
    }

}
  • 使用Demo
@Resource
private RestTemplate restTemplate;

/**
 * RestTemplate提供了直接发起GET、POST等HTTP请求方法,同时也提供直接发起exchange方法来发起HTTP请求的方法。具体可以在使用时直接查看方法说明
 */
public void test() {
    String url="http://xxxx";
    // RestTemplate提供了直接发起GET、POST等HTTP请求方法,例如
    restTemplate.getForEntity(url,String.class);
    restTemplate.postForObject(url,String.class);
    // RestTemplate也提供了直接发起exchange方法来发起HTTP请求
    restTemplate.exchange(url,httpMethod,httpEntity,org.springframework.core.io.Resource.class)
}


版权声明:本文为Knight_hf原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。