工作中使用连接池的HttpClient工具类

httpclient4.4简单初始化httpclient的方式:

HttpClient httpClient = HttpClientBuilder.create().build();
        ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        restTemplate = new RestTemplate(requestFactory);

上面是没有使用池子进行初始化httpclient,在自己做demo的时候使用这种方式完全没有问题的,但是这种方式建立连接比较的浪费时间和资源,尤其生产环境访问量大,并发数多的情况,如果没有管理这些连接的工具,总是存在不安全的隐患。像一些比较耗资源的中间件连接都是有配置连接池的,例如redis,数据库,线程池等等,都是有相关池子的配置的,功能几乎一样的——为了避免频繁建立连接造成的时间、资源等浪费。

本博客提供了一个使用池子构建httpclient的工具类,该工具类不光对连接池进行了设置,还有其他相关配置,下面这块是核心代码:

public static CloseableHttpClient getHttpClinet() throws Exception {
		CloseableHttpClient httpClient = HttpClients.custom()
				//设置ssl工厂
				.setSSLSocketFactory(sslConnectionSocketFactory)
				//设置连接管理方式-连接池
				.setConnectionManager(poolingHttpClientConnectionManager)
				//设置http请求规则
				.setDefaultRequestConfig(getDefaultRequestConfig())
				//设置keep-Alive
				.setKeepAliveStrategy(getKeepAliveStrategy())
				.build();
		return httpClient;
	}

合理的初始化一个httpClient需要设置4个配置:

  1. 设置ssl工厂,访问https时是必备的,访问http时是不需要的
  2. 设置连接管理方式-连接池(连接方式有很多种:SimpleHttpConnectionManager、MultiThreadedHttpConnectionManager、poolingHttpClientConnectionManager),本工具类使用poolingHttpClientConnectionManager
  3. 设置keep-Alive超时时间,在高频的情况下这个是有必要的,因为httpclient是要放回池子里的,如果调用的服务端没有设置或者设置过长的keep-alive时间,会把这个未关闭长连接的httpclient返回池子,如果再次使用会报The server ***** failed to respond.的错误。

工具类:
里面的注释很详细,也做了相应的优化 ,可以直接使用

package com.sgcc.base.https;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.http.Consts;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
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.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sgcc.Exception.BusinessException;

/**
 * @Title: HttpsPoolUtil.java
 * @Description: TODO(用一句话描述该文件做什么)
 * @date 2019年12月24日 下午6:09:11
 * @version V1.0
 */
public class HttpsPoolUtil {

	private static Logger log = LoggerFactory.getLogger(HttpsPoolUtil.class);
	private static final String _HTTP = "http";
	private static final String _HTTPS = "https";
	
	//配置连接池获取超时时间
	private static final int CONNECTION_REQUEST_TIMEOUT= 1 * 1000; 
	//配置客户端连接服务器超时时间
    private static final int CONNECT_TIMEOUT =  3 * 1000;
    //配置服务器响应超时时间
    private static final int SOCKET_TIMEOUT = 20 * 1000;
    //默认返回null串
  	private static String EMPTY_STR = "";
	
	private static SSLConnectionSocketFactory sslConnectionSocketFactory = null;
	//连接池管理类
	private static PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = null;
	//管理Https连接的上下文类
	private static SSLContextBuilder sslContextBuilder = null;
	
	static {
		try {
			sslContextBuilder = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
				@Override
				public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
					//忽略http校验
					return true;
				}
			});
			sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build(), new String[] { "SSLv3", "TLSv1", "TLSv1.2" }, null, null);
			//注册两种请求形式
			Registry<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create()
					.register(_HTTP, new PlainConnectionSocketFactory())
					.register(_HTTPS, sslConnectionSocketFactory).build();
			poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registryBuilder);
			//最大连接数
			poolingHttpClientConnectionManager.setMaxTotal(500);
			//最大并发数
			poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (KeyStoreException e) {
			e.printStackTrace();
		} catch (KeyManagementException e) {
			e.printStackTrace();
		}

	}

	/**
	 * @Description: http初始化连接配置
	 * @date 2019年12月25日 下午5:09:17
	 * @param httpRequestBase
	 */
	private static RequestConfig getDefaultRequestConfig() {
		RequestConfig requestConfig = RequestConfig.custom()
				/*
				 * 从连接池中获取连接的超时时间,假设:连接池中已经使用的连接数等于setMaxTotal,新来的线程在等待1*1000
				 * 后超时,错误内容:org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
				 */
				.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
				/*
				 * 这定义了通过网络与服务器建立连接的超时时间。
				 * Httpclient包中通过一个异步线程去创建与服务器的socket连接,这就是该socket连接的超时时间,
				 * 此处设置为2秒。假设:访问一个IP,192.168.10.100,这个IP不存在或者响应太慢,那么将会返回
				 * java.net.SocketTimeoutException: connect timed out
				 */
				.setConnectTimeout(CONNECT_TIMEOUT)
				/*
				 * 指的是连接上一个url,获取response的返回等待时间,假设:url程序中存在阻塞、或者response
				 * 返回的文件内容太大,在指定的时间内没有读完,则出现
				 * java.net.SocketTimeoutException: Read timed out
				 */
				.setSocketTimeout(SOCKET_TIMEOUT)
				.build();
		return requestConfig;
	}

	/**
	 * @Description: http初始化keep-Alive配置
	 * @date 2019年12月25日 下午5:09:17
	 * @param httpRequestBase
	 */
	public static ConnectionKeepAliveStrategy getKeepAliveStrategy(){
		ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
		    @Override
		    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
		        HeaderElementIterator it = new BasicHeaderElementIterator
		    (response.headerIterator(HTTP.CONN_KEEP_ALIVE));
		        while (it.hasNext()) {
		            HeaderElement he = it.nextElement();
		            String param = he.getName();
		            String value = he.getValue();
		            if (value != null && param.equalsIgnoreCase
		               ("timeout")) {
		                return Long.parseLong(value) * 1000;
		            }
		        }
		        return 20 * 1000;//如果没有约定,则默认定义时长为20s
		    }
		};
		return myStrategy;
	}
	
	/**
	 * @Description: 从池子中获取获取httpclient连接
	 * @date 2019年12月25日 下午5:12:43
	 * @return
	 * @throws Exception
	 */
	public static CloseableHttpClient getHttpClinet() throws Exception {
		CloseableHttpClient httpClient = HttpClients.custom()
				//设置ssl工厂
				.setSSLSocketFactory(sslConnectionSocketFactory)
				//设置连接管理方式-连接池
				.setConnectionManager(poolingHttpClientConnectionManager)
				//设置http请求规则
				.setDefaultRequestConfig(getDefaultRequestConfig())
				//设置keep-Alive
				.setKeepAliveStrategy(getKeepAliveStrategy())
				.build();
		return httpClient;
	}

	/**
	 * @Description: post请求——JSON格式
	 * @date 2019年12月25日 下午2:10:34
	 * @param url
	 * @param json
	 * @return
	 */
	public static String postJSON(String url, String json) {
		
		HttpPost httpPost = new HttpPost(url);
		StringEntity entity = new StringEntity(json, Consts.UTF_8);//解决中文乱码问题
		entity.setContentType("application/json;charset=UTF-8");
		httpPost.setEntity(entity);
		return getResult(httpPost);
	}

	/**
	 * @Description: post请求——form格式
	 * @date 2019年12月25日 下午2:10:34
	 * @param url
	 * @param json
	 * @return
	 */
	public static String postForm(String url, Map<String,String> params) {
		
		HttpPost httpPost = new HttpPost(url);
		//拼装参数,设置编码格式
		if (MapUtils.isNotEmpty(params)) {
			List<NameValuePair> paramList = new ArrayList<>();
			for (Map.Entry<String, String> stringStringEntry : params.entrySet()) {
				paramList.add(new BasicNameValuePair(stringStringEntry.getKey(), stringStringEntry.getValue()));
			}
			UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(paramList, Consts.UTF_8);
			httpPost.setEntity(urlEncodedFormEntity);
		}
		return getResult(httpPost);
	}

	/**
	 * @Description: 通用版处理http请求
	 * @date 2019年12月25日 下午4:56:35
	 * @param request
	 * @return
	 */
	private static String getResult(HttpRequestBase request) {

		/**
		 * 获取httpClient
		 */
		CloseableHttpClient httpClient = null;
		try {
			//获取httpClient
			httpClient = getHttpClinet();
		} catch (Exception e) {
			log.error("【新版http】获取httpClient失败:请求地址:{},异常信息:", request.getURI(),e);
			throw new BusinessException(-1, "获取httpClient失败");
		}
		/**
		 * 发起http请求,并处理响应结果
		 */
		String resultStr = null;
		CloseableHttpResponse httpResponse = null;
		try {
			//发起http请求
			httpResponse = httpClient.execute(request);
			int statusCode = httpResponse.getStatusLine().getStatusCode();
			//响应成功
			if (statusCode == HttpStatus.SC_OK) {
				HttpEntity httpResponseEntity = httpResponse.getEntity();
				resultStr = EntityUtils.toString(httpResponseEntity);
				log.info("【新版http】请求正常,请求地址:{},响应结果:{}", request.getURI(), resultStr);
				return resultStr;
			}
			//响应失败,打印http异常信息
			StringBuffer stringBuffer = new StringBuffer();
			HeaderIterator headerIterator = httpResponse.headerIterator();
			while (headerIterator.hasNext()) {
				stringBuffer.append("\t" + headerIterator.next());
			}
			log.info("【新版http】异常信息:请求地址:{},响应状态:{},请求返回结果:{}", request.getURI(), statusCode, stringBuffer);
		} catch (Exception e) {
			log.error("【新版http】发生异常:请求地址:{},异常信息:", request.getURI(), e);
			throw new BusinessException(-1, "http请求失败");
		} finally {
			//关闭httpResponse
			if (httpResponse != null) {
				try {
					httpResponse.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return EMPTY_STR;
	}
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值