`
QING____
  • 浏览: 2232761 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Apache HttpComponents客户端编程样例

    博客分类:
  • JAVA
阅读更多

     本文展示基于apache httpComponents实现的REST客户端,可以作为RestTemplate底层的实现支撑。

    1、基于HttpClient 4.5 + 

    2、仅实现同步调用。

    3、通用客户端,仅支持Rest调用,即请求和响应均为文本类型。

 

    类列表:

    1、HttpClientConfiguration:客户端的主要配置,包括连接超时、连接池大小等。

    2、HttpClientTemplate:接口,描述了客户端支持的基本操作。

    3、AbstractHttpClientTemplate:2、接口的基本实现,主要用于构建默认参数和方法。

    4、HttpComponentsClientTemplate:主要实现类,用户描述GET/POST操作的过程。

    5、HttpComponentsClientBuilder:静态类,用于外部便捷的构建client,可以被spring、springboot集成(自动装配时使用)。

 

 

一、HttpClientConfiguration

/**
 * HTTP Client配置管理
 **/
public class HttpClientConfiguration implements Serializable {

    //目前不能修改,常量值
    private static final int DEFAULT_MAX_CONNECTIONS = 1024;

    //连接创建
    public static final long DEFAULT_CONNECTION_TIMEOUT = 500;
    //SO_TIMEOUT
    public static final long DEFAULT_SOCKET_TIMEOUT = 500;

    //连接池中的连接被保活的时长
    public static final long DEFAULT_KEEP_ALIVE_TIME = 6000;

    //请求异常(exception),重试的次数,默认为0,不重试
    public static final int DEFAULT_HTTP_RETRY_TIMES = 0;
    //是否重试
    public static final boolean DEFAULT_HTTP_RETRY_ON_FAILURE = false;

    public static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = 1500;

    public static final String HTTP_CLIENT_TYPE_OK_HTTP = "okHttp";

    public static final String HTTP_CLIENT_TYPE_HTTP_COMPONENTS = "httpComponents";

    //连接创建
    private long connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
    //SO_TIMEOUT
    private long socketTimeout = DEFAULT_SOCKET_TIMEOUT;

    //连接池中的连接被保活的时长
    private long keepAliveTime = DEFAULT_KEEP_ALIVE_TIME;

    //请求异常(exception),重试的次数,默认为0,不重试
    private int retryTimes = DEFAULT_HTTP_RETRY_TIMES;
    //是否重试
    private boolean retryOnFailure = DEFAULT_HTTP_RETRY_ON_FAILURE;

    private String charset = ContextConstants.DEFAULT_CHARSET;

    //全局headers,即所有请求都会添加
    private Map<String, String> globalHeaders;

    private int connectionRequestTimeout = 1500;

    private String clientType = HTTP_CLIENT_TYPE_HTTP_COMPONENTS;

    public void setConnectionTimeout(Long connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

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

    public void setKeepAliveTime(Long keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
    }

    public void setRetryTimes(Integer retryTimes) {
        this.retryTimes = retryTimes;
    }

    public void setRetryOnFailure(Boolean retryOnFailure) {
        this.retryOnFailure = retryOnFailure;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public Long getConnectionTimeout() {
        return connectionTimeout;
    }

    public Long getSocketTimeout() {
        return socketTimeout;
    }

    public Long getKeepAliveTime() {
        return keepAliveTime;
    }

    public Integer getRetryTimes() {
        return retryTimes;
    }

    public Boolean getRetryOnFailure() {
        return retryOnFailure;
    }

    public String getCharset() {
        return charset;
    }

    public Map<String, String> getGlobalHeaders() {
        return globalHeaders;
    }

    public void setGlobalHeaders(Map<String, String> globalHeaders) {
        this.globalHeaders = globalHeaders;
    }

    public int getMaxConnections() {
        return DEFAULT_MAX_CONNECTIONS;
    }

    public int getConnectionRequestTimeout() {
        return connectionRequestTimeout;
    }


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

    public String getClientType() {
        return clientType;
    }

    public void setClientType(String clientType) {
        this.clientType = clientType;
    }

    private HttpClientConfiguration() {
    }

    /**
     * 构建一个常规配置
     *
     * @param globalHeaders
     * @return
     */
    public static HttpClientConfiguration common(Map<String, String> globalHeaders) {
        HttpClientConfiguration configuration = new HttpClientConfiguration();
        configuration.setCharset(ContextConstants.DEFAULT_CHARSET);//UTF-8
        configuration.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);//500
        configuration.setSocketTimeout(DEFAULT_SOCKET_TIMEOUT);//500
        configuration.setKeepAliveTime(DEFAULT_KEEP_ALIVE_TIME);//6000
        configuration.setRetryOnFailure(DEFAULT_HTTP_RETRY_ON_FAILURE);//FALSE
        configuration.setRetryTimes(DEFAULT_HTTP_RETRY_TIMES);//0
        if (globalHeaders != null) {
            configuration.setGlobalHeaders(globalHeaders);
        }
        configuration.setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT);
        return configuration;
    }

    public static HttpClientConfiguration common() {
        return common(null);
    }


}

 

    1)connectionTimeout、socketTimeout,我们约定为500ms,对于普通WEB接口而言,此参数为权衡值,你可以根据自己的业务现状适度调整。

    2)keepAliveTime:连接池中的连接被保活的时长,默认为6000ms,不建议设置太大。

    3)retryOnFailure:当请求调用失败时,是否支持重试,失败主要为IO异常,强烈建议此值为false。

    4)retryTimes:当retryOnFailure=true时,失败重试的次数,建议为0。

    5)连接池容量,此处为1024,大家一定要合理修改此值。默认值太小,容易导致问题。

 

二、HttpClientTemplate

public interface HttpClientTemplate extends BeanLifeCycle {

    /**
     * 普通get操作
     *
     * @param uri 不应该包含查询字符串
     * @return
     */
    public String get(String uri);

    /**
     * @param uri        不包含查询字符串
     * @param parameters 查询字符串,编码之前原始字符串,内部会提供编码和转义。默认UTF-8
     * @return
     */
    public String get(String uri, Map<String, String> parameters);

    /**
     * @param uri        不应该包含查询字符串
     * @param parameters 查询字符串,编码之前的原始字符串
     * @param headers    http header
     * @return
     */
    public String get(String uri, Map<String, String> parameters, Map<String, String> headers);

    /**
     * 普通post
     *
     * @param uri 不应该包含查询字符串
     * @return
     */
    public String post(String uri);

    /**
     * 数据将通过body发送
     *
     * @param uri     不应该包含查询字符串
     * @param content JSON内容文本,应该使用与client相同的编码进行
     * @return
     */
    public String post(String uri, String content);

    /**
     * 数据将通过body发送
     *
     * @param uri        不应该包含查询字符串
     * @param content    JSON格式文本
     * @param parameters 查询字符串
     * @return
     */
    public String post(String uri, String content, Map<String, String> parameters);

    /**
     * 数据将通过body发送
     *
     * @param uri        不应该包含查询字符串
     * @param content    JSON格式文本,body
     * @param parameters 查询字符串
     * @param headers    headers
     * @return
     */
    public String post(String uri, String content, Map<String, String> parameters, Map<String, String> headers);

    /**
     * 返回底层的httpClient,通常情况是用户希望直接使用此client做自定义操作。
     *
     * @return
     */
    public Object getClient();

    public String clientType();
}

 

 

三、AbstractHttpClientTemplate

public abstract class AbstractHttpClientTemplate implements HttpClientTemplate {

    //全局headers,即所有请求都会添加,可以被用户指定的headers覆盖
    protected Map<String, String> globalHeaders;

    protected static final String CONTENT_TYPE_JSON = "application/json";//default
    //json格式,限定
    protected static final String CONTENT_TYPE_JSON_PATTERN = CONTENT_TYPE_JSON + "; charset={0}";//default

    protected HttpClientConfiguration configuration = HttpClientConfiguration.common();

    protected String charset = configuration.getCharset();

    private volatile boolean init = false;

    public AbstractHttpClientTemplate() {
    }

    public AbstractHttpClientTemplate(HttpClientConfiguration configuration) {
        if (configuration != null) {
            this.configuration = configuration;
            this.globalHeaders = configuration.getGlobalHeaders();
        }
    }

    public void setGlobalHeaders(Map<String, String> globalHeaders) {
        this.globalHeaders = globalHeaders;
        configuration.setGlobalHeaders(globalHeaders);
    }

    public synchronized void init() {
        if (init) {
            return;
        }
        init = true;
    }

    protected HttpClientConfiguration configuration() {
        return this.configuration;
    }

}

 

 

四、HttpComponentsClientTemplate实现类

import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 基于HttpComponents实现的客户端,只支持String类型请求,即REST
 **/
public class HttpComponentsClientTemplate extends AbstractHttpClientTemplate {

    private CloseableHttpClient httpClient;

    private volatile boolean init = false;

    private List<HttpRequestInterceptor> requestInterceptors;

    private List<HttpResponseInterceptor> responseInterceptors;

    public void setRequestInterceptors(List<HttpRequestInterceptor> requestInterceptors) {
        this.requestInterceptors = requestInterceptors;
    }

    public void setResponseInterceptors(List<HttpResponseInterceptor> responseInterceptors) {
        this.responseInterceptors = responseInterceptors;
    }

    public HttpComponentsClientTemplate() {
        super();
    }

    public HttpComponentsClientTemplate(HttpClientConfiguration configuration) {
        super(configuration);
    }

    @Override
    public synchronized void init() {
        if (init) {
            return;
        }
        super.init();
        httpClient = HttpComponentsClientBuilder.build(configuration(), requestInterceptors, responseInterceptors);
        init = true;
    }

    private void parameters(RequestBuilder requestBuilder, Map<String, String> parameters) {
        if (parameters == null || parameters.isEmpty()) {
            return;
        }

        for (Map.Entry<String, String> entry : parameters.entrySet()) {
            String value = entry.getValue() == null ? "" : entry.getValue();
            requestBuilder.addParameters(new BasicNameValuePair(entry.getKey(), value));
        }
    }

    private void headers(RequestBuilder requestBuilder, Map<String, String> headers) {
        if (headers == null && globalHeaders == null) {
            return;
        }

        Map<String, String> _headers = new HashMap<>();
        if (globalHeaders != null && !globalHeaders.isEmpty()) {
            _headers.putAll(globalHeaders);
        }
        if (headers != null && !headers.isEmpty()) {
            _headers.putAll(headers);
        }

        for (Map.Entry<String, String> entry : _headers.entrySet()) {
            String value = entry.getValue() == null ? "" : entry.getValue();
            requestBuilder.addHeader(entry.getKey(), value);
        }
    }

    @Override
    public String get(String uri) {
        return get(uri, null);
    }

    @Override
    public String get(String uri, Map<String, String> parameters) {
        return get(uri, parameters, null);
    }

    @Override
    public String get(String uri, Map<String, String> parameters, Map<String, String> headers) {
        if (StringUtils.isBlank(uri)) {
            return null;
        }
        try {
            RequestBuilder requestBuilder = RequestBuilder.get(uri);
            parameters(requestBuilder, parameters);
            //对于get请求,增补一个常规header
            String contentType = MessageFormat.format(CONTENT_TYPE_JSON_PATTERN, charset);
            requestBuilder.addHeader(HttpHeaders.CONTENT_TYPE, contentType);
            //用户自定义header(可以覆盖增补的header)
            headers(requestBuilder, headers);

            requestBuilder.setCharset(Charset.forName(charset));

            HttpUriRequest request = requestBuilder.build();
            return process(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String post(String uri) {
        return post(uri, null, null);
    }

    @Override
    public String post(String uri, String content) {
        return post(uri, content, null);
    }

    @Override
    public String post(String uri, String content, Map<String, String> parameters) {
        return post(uri, content, parameters, null);
    }

    @Override
    public String post(String uri, String content, Map<String, String> parameters, Map<String, String> headers) {
        if (StringUtils.isBlank(uri)) {
            return null;
        }
        try {
            RequestBuilder requestBuilder = RequestBuilder.post(uri);
            parameters(requestBuilder, parameters);
            requestBuilder.setCharset(Charset.forName(charset));
            if (content != null) {
                StringEntity stringEntity = new StringEntity(content, ContentType.create(CONTENT_TYPE_JSON, charset));
                requestBuilder.setEntity(stringEntity);
            } else {
                String contentType = MessageFormat.format(CONTENT_TYPE_JSON_PATTERN, charset);
                requestBuilder.addHeader(HttpHeaders.CONTENT_TYPE, contentType);
            }

            headers(requestBuilder, headers);

            HttpUriRequest request = requestBuilder.build();
            return process(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


    private String process(HttpUriRequest request) throws Exception {
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(request);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if (statusCode != 200) {
                request.abort();
                throw new RuntimeException("HttpClient error, status=" + statusCode + ",message:" + statusLine.getReasonPhrase());
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, charset);
            }
            EntityUtils.consume(entity);
            return result;
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }

    @Override
    public void close() throws IOException {
        if (httpClient != null) {
            httpClient.close();
        }
    }

    @Override
    public HttpClient getClient() {
        return httpClient;
    }


    @Override
    public String clientType() {
        return HttpClientConfiguration.HTTP_CLIENT_TYPE_HTTP_COMPONENTS;
    }


}

 

五、HttpComonentsClientBuilder构建类

import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;

import java.util.List;
import java.util.concurrent.TimeUnit;


public final class HttpComponentsClientBuilder {

    /**
     * used HttpClient
     *
     * @return
     */
    public static CloseableHttpClient build(HttpClientConfiguration configuration) {
        return build(configuration, null, null);
    }


    public static CloseableHttpClient build(HttpClientConfiguration configuration, List<HttpRequestInterceptor> requestInterceptors, List<HttpResponseInterceptor> responseInterceptors) {
        HttpClientBuilder builder = HttpClientBuilder.create();

        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(configuration.getRetryTimes(), configuration.getRetryOnFailure());
        builder.setRetryHandler(retryHandler);
        builder.setConnectionTimeToLive(configuration.getKeepAliveTime(), TimeUnit.MILLISECONDS);
        if (requestInterceptors != null) {
            for (HttpRequestInterceptor interceptor : requestInterceptors) {
                builder.addInterceptorLast(interceptor);
            }
        }

        if (responseInterceptors != null) {
            for (HttpResponseInterceptor interceptor : responseInterceptors) {
                builder.addInterceptorLast(interceptor);
            }
        }

        //connectionRequestTimeout:
        //连接池模式时,从pool中获取连接的阻塞最大时长。默认是连接池,默认永久阻塞。
        //RT最大为1.5S
        //连接数最大为1024
        //每个域名最多连接数64
        //如果上述值,需要修改,请用户自己手动创建客户端,
        // 言外之意,这些参数是默认规范
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(configuration.getConnectionTimeout().intValue())
                .setSocketTimeout(configuration.getSocketTimeout().intValue())
                .setConnectionRequestTimeout(configuration.getConnectionRequestTimeout())//请求最大时长1.5S,
                .build();
        builder.setDefaultRequestConfig(defaultRequestConfig);
        builder.setMaxConnTotal(configuration.getMaxConnections());
        builder.setMaxConnPerRoute(128);//常量值
        builder.evictExpiredConnections();//过期移除
        builder.evictIdleConnections(10, TimeUnit.SECONDS);//空闲10秒移除

        return builder.build();
    }

    /**
     * 基于常规默认
     *
     * @return
     */
    public static CloseableHttpClient build() {
        HttpClientConfiguration configuration = HttpClientConfiguration.common();
        return build(configuration);
    }
}

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics