HttpClient性能优化方案

Java的复杂应用开发过程中经常遇到跨系统的数据访问活动(例如业务系统访问基础数据、校验票据、验证身份等等),系统和系统之间的数据连接和传输是制约服务器响应速度的关键。

1.前言

       目前HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。HttpClient Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。简单业务访问时,一般的每次请求时会初创建一个httpclient,执行httpPost对象或者httpGet对象,然后从返回结果取出entity,最后关闭response释放链接。然而在高并发业务请求时,这个过程无疑会消耗大量资源,需要对HttpClient进行优化,提高服务器响应速度。

2.优化策略:

1)       Httpclient是一个线程安全的类,没必要每次请求时都重新创建,使用单例模式创建一个全局的HttpClient即可。

2)       tcp的三次握手与四次挥手过程,在高频次的情况下消耗实在太大,可以采用Http连接池减少这部分时间损耗。同时能够合理利用资源,支持更大的并发,综合提升系统响应速度。

3)       创建监测线程,及时关闭被服务器单向断开的连接。

3.代码分析

package com.ceshi.common.util;

import com.alibaba.fastjson.JSON;
import com.xdja.pcs.common.exception.ServiceException;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
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.LayeredConnectionSocketFactory;
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.BasicHttpEntity;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
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.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 javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
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.Map;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * EffectiveHttp
 *
 * @author xx
 * @date 2019/3/1 1:15
 */
public class EffectiveHttp {

	/**
	 * static logger
	 */
	private static final Logger logger = LoggerFactory.getLogger(EffectiveHttp.class);

	/**
	 * 保活时间
	 */
	private static int keepAlive = 30;

	/**
	 * 连接池最大连接数
	 */
	private static final int maxTotal = 1000;

	/**
	 * 路由的默认最大连接
	 */
	private static final int defaultMaxPerRoute = 400;

	/**
	 * 连接超时时间
	 */
	private static final int maxIdleTime = 30000;

	/**
	 * 请求超时时间
	 */
	private static final int connectTimeout = 30000;

	/**
	 * 获取数据超时时间
	 */
	private static final int socketTimeout = 30000;

	/**
	 * 保活连接策略
	 */
	private static ConnectionKeepAliveStrategy keepAliveStrategy;

	/**
	 * Http连接池
	 */
	private static PoolingHttpClientConnectionManager connectionManager;

	/**
	 * clientBuilder
	 */
	private static HttpClientBuilder clientBuilder;

	/**
	 * httpClient
	 */
	private volatile static CloseableHttpClient httpClient;

	/**
	 * monitor Thread
	 */
	private static ScheduledExecutorService monitorExecutor;

	/**
	 * 创建get请求
	 *
	 * @param url url
	 * @return get工具
	 */
	public static EffectiveHttp.GetUtil createGet(String url) {
		return new EffectiveHttp.GetUtil(url);
	}

	/**
	 * 创建post请求
	 *
	 * @param url url
	 * @return post请求工具
	 */
	public static EffectiveHttp.PostUtil createPost(String url) {
		return new EffectiveHttp.PostUtil(url);
	}

	/**
	 * 创建post请求
	 *
	 * @param url url
	 * @return post请求工具
	 */
	public static EffectiveHttp.PostUtil createPost(String url, ContentType contentType) {
		return new EffectiveHttp.PostUtil(url, contentType);
	}

	/**
	 * 创建form表单请求
	 *
	 * @param url url
	 * @return form表单工具
	 */
	public static EffectiveHttp.FormUtil createForm(String url) {
		return new EffectiveHttp.FormUtil(url);
	}

	/**
	 * 初始化参数
	 */
	static {
		init();
	}

	/**
	 * 初始化Http配置
	 */
	private static void init() {
		logger.debug("@EffectiveHttp-------初始化Http连接池配置......");

		connectionManager = new PoolingHttpClientConnectionManager(getRegistry());
		connectionManager.setMaxTotal(maxTotal);
		connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);

		keepAliveStrategy = getKeepAliveStrategy();

		clientBuilder = createClientBuilder();
	}

	/**
	 * 创建 HttpClient实例(单例模式)
	 */
	public static CloseableHttpClient getInstance() {

		if (httpClient == null) {
			synchronized (EffectiveHttp.class) {
				if (httpClient == null) {
					httpClient = clientBuilder.build();
					//开启监控线程,对异常和空闲线程进行关闭
					monitorExecutor = Executors.newScheduledThreadPool(1);
					monitorExecutor.scheduleAtFixedRate(new TimerTask() {
						@Override
						public void run() {
							//关闭异常连接
							connectionManager.closeExpiredConnections();
							//关闭空闲的连接
							connectionManager.closeIdleConnections(maxIdleTime, TimeUnit.SECONDS);
						}
					}, 30, 30, TimeUnit.SECONDS);
				}
			}
		}
		return httpClient;
	}

	/**
	 * 执行http请求
	 *
	 * @param requestBase request请求
	 * @return 封装好的请求结果
	 */
	private static EffectiveHttp.ResponseWrap exec(HttpRequestBase requestBase) {
		EffectiveHttp.ResponseWrap responseWrap = null;
		try {
			httpClient = getInstance();
			HttpClientContext context = HttpClientContext.create();
			CloseableHttpResponse execute = httpClient.execute(requestBase, context);
			responseWrap = new EffectiveHttp.ResponseWrap(execute);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return responseWrap;
	}

	/**
	 * HttpClientBuilder
	 */
	public static HttpClientBuilder createClientBuilder() {
		HttpClientBuilder clientBuilder = HttpClients.custom()
				.setConnectionManager(connectionManager)
				.setKeepAliveStrategy(keepAliveStrategy);
		return clientBuilder;
	}

	/**
	 * 连接策略
	 *
	 * @return
	 */
	private static ConnectionKeepAliveStrategy getKeepAliveStrategy() {
		ConnectionKeepAliveStrategy keepAliveStrategy = 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 && "timeout".equalsIgnoreCase
							(param)) {
						return Long.parseLong(value) * 1000;
					}
				}
				return keepAlive * 1000;
			}
		};
		return keepAliveStrategy;
	}

	/**
	 * @return
	 */
	private static Registry getRegistry() {
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("https", getSSLSocketFactory())
				.register("http", new PlainConnectionSocketFactory())
				.build();
		return socketFactoryRegistry;
	}

	/**
	 * 构建sslSocketFactory
	 *
	 * @return sslFactory
	 */
	private static LayeredConnectionSocketFactory getSSLSocketFactory() {
		try {
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
				//信任所有
				@Override
				public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
					return true;
				}
			}).build();
			return new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * get请求工具类
	 */
	public static class GetUtil {
		/**
		 * 请求参数
		 */
		private HttpGet httpGet;
		/**
		 * get url builder
		 */
		private URIBuilder uriBuilder;
		/**
		 * request config builder
		 */
		private RequestConfig.Builder requestConfigBuilder;

		private GetUtil(String url) {
			httpGet = new HttpGet(url);
			uriBuilder = new URIBuilder().setPath(httpGet.getURI().toString());
			uriBuilder.setCharset(Charset.forName("UTF-8"));
			requestConfigBuilder = RequestConfig.custom().setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout);
		}

		/**
		 * 添加header
		 *
		 * @param name  name
		 * @param value value
		 * @return this
		 */
		public EffectiveHttp.GetUtil addHeader(String name, String value) {
			httpGet.addHeader(name, value);
			return this;
		}

		/**
		 * 添加请求参数
		 *
		 * @param param param
		 * @param value value
		 * @return this
		 */
		public EffectiveHttp.GetUtil addParameter(String param, String value) {
			uriBuilder.setParameter(param, value);
			return this;
		}

		/**
		 * 设置超时时间(该模块待抽象出去)
		 *
		 * @param timeout timeOut mills
		 * @return this
		 */
		public EffectiveHttp.GetUtil setConnectTimeOut(int timeout) {
			requestConfigBuilder.setConnectTimeout(timeout);
			return this;
		}

		/**
		 * 设置socketTimeOut(该模块待抽象出去)
		 *
		 * @param timeout timeout mills
		 * @return this
		 */
		public EffectiveHttp.GetUtil setSocketTimeout(int timeout) {
			requestConfigBuilder.setSocketTimeout(timeout);
			return this;
		}

		/**
		 * 执行并返回结果
		 */
		public EffectiveHttp.ResponseWrap execute() {
			try {
				URI uri = new URI(uriBuilder.build().toString().replace("%3F", "?"));// replace %3F -> ?
				httpGet.setURI(uri);
				httpGet.setConfig(requestConfigBuilder.build());
			} catch (URISyntaxException e) {
				logger.error(e.getMessage(), e);
			}

			return exec(httpGet);
		}
	}

	/**
	 * post请求
	 */
	public static class PostUtil {
		/**
		 * 请求参数
		 */
		private HttpPost httpPost;
		/**
		 * entityBuilder
		 */
		private EntityBuilder entityBuilder;
		/**
		 * request config builder
		 */
		private RequestConfig.Builder requestConfigBuilder;

		public PostUtil(String url) {
			httpPost = new HttpPost(url);
			entityBuilder = EntityBuilder.create().setParameters(new ArrayList<NameValuePair>());
			requestConfigBuilder = RequestConfig.custom().setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout);
		}

		/**
		 * 带contentType的post
		 *
		 * @param url         url
		 * @param contentType 类型
		 */
		public PostUtil(String url, ContentType contentType) {
			this(url);
			entityBuilder.setContentType(contentType);
		}

		/**
		 * 设置超时时间(该模块待抽象出去)
		 *
		 * @param timeout timeOut mills
		 * @return this
		 */
		public EffectiveHttp.PostUtil setConnectTimeOut(int timeout) {
			requestConfigBuilder.setConnectTimeout(timeout);
			return this;
		}

		/**
		 * 设置socketTimeOut(该模块待抽象出去)
		 *
		 * @param timeout timeout mills
		 * @return this
		 */
		public EffectiveHttp.PostUtil setSocketTimeout(int timeout) {
			requestConfigBuilder.setSocketTimeout(timeout);
			return this;
		}

		/**
		 * 添加header
		 *
		 * @param name  name
		 * @param value value
		 * @return this
		 */
		public EffectiveHttp.PostUtil addHeader(String name, String value) {
			httpPost.addHeader(name, value);
			return this;
		}

		/**
		 * 添加请求参数
		 *
		 * @param param param
		 * @param value value
		 * @return this
		 */
		public EffectiveHttp.PostUtil addParameter(String param, String value) {
			entityBuilder.getParameters().add(new BasicNameValuePair(param, value));
			return this;
		}

		/**
		 * 添加请求参数列表
		 *
		 * @param parameters 参数列表
		 * @return this
		 */
		public EffectiveHttp.PostUtil addParameters(Map<String, String> parameters) {
			for (Map.Entry<String, String> e : parameters.entrySet()) {
				addParameter(e.getKey(), e.getValue());
			}
			return this;
		}

		/**
		 * 添加jsonBody
		 *
		 * @param object object
		 * @return this
		 */
		public EffectiveHttp.PostUtil addJsonBody(Object object) {
			entityBuilder.setContentType(ContentType.APPLICATION_JSON);
			entityBuilder.setContentEncoding("utf-8");
			try {
				entityBuilder.setBinary(JSON.toJSONString(object).getBytes("UTF-8"));
			} catch (Exception e) {
				throw new RuntimeException("@EffectiveHttp-------charset error!");
			}
			return this;
		}

		/**
		 * 添加二进制body
		 *
		 * @param bytes bytes
		 * @return postUtil
		 */
		public EffectiveHttp.PostUtil addBody(byte[] bytes) {
			entityBuilder.setBinary(bytes);
			return this;
		}

		/**
		 * 执行 请求
		 *
		 * @return responseWrap
		 */
		public EffectiveHttp.ResponseWrap execute() {
			HttpEntity httpEntity = entityBuilder.build();
			httpPost.setEntity(httpEntity);
			httpPost.setConfig(requestConfigBuilder.build());
			return exec(httpPost);
		}
	}

	/**
	 * form表单提交
	 */
	public static class FormUtil {
		/**
		 * httpPost
		 */
		private HttpPost httpPost;
		/**
		 * form builder
		 */
		private MultipartEntityBuilder builder;

		public FormUtil(String url) {
			httpPost = new HttpPost(url);
			builder = MultipartEntityBuilder.create();
			builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
		}

		/**
		 * 添加普通参数
		 *
		 * @param name  name
		 * @param value value
		 * @return this
		 */
		public EffectiveHttp.FormUtil addParameter(String name, String value) {
			builder.addTextBody(name, value, ContentType.TEXT_PLAIN.withCharset(Charset.forName("UTF-8")));
			return this;
		}

		/**
		 * 添加普通参数list
		 *
		 * @param parameters normal parameter list
		 * @return this
		 */
		public EffectiveHttp.FormUtil addParameters(Map<String, String> parameters) {
			for (Map.Entry<String, String> e : parameters.entrySet()) {
				addParameter(e.getKey(), e.getValue());
			}
			return this;
		}

		/**
		 * 添加字节流
		 *
		 * @param name  name
		 * @param bytes 字节流
		 * @return this
		 */
		public EffectiveHttp.FormUtil addParameter(String name, byte[] bytes) {
			builder.addBinaryBody(name, bytes);
			return this;
		}

		/**
		 * 添加二进制流
		 *
		 * @param name        参数名称
		 * @param bytes       参数值
		 * @param contentType 参数类型
		 * @param filename    文件名
		 * @return this
		 */
		public EffectiveHttp.FormUtil addParameter(String name, byte[] bytes, ContentType contentType, String filename) {
			builder.addBinaryBody(name, bytes, contentType, filename);
			return this;
		}

		/**
		 * 添加文件
		 *
		 * @param name        参数名称
		 * @param file        文件
		 * @param contentType contentType
		 * @param fileName    文件名称
		 * @return this
		 */
		public EffectiveHttp.FormUtil addParameter(String name, File file, ContentType contentType, String fileName) {
			builder.addBinaryBody(name, file, contentType, fileName);
			return this;
		}

		/**
		 * 添加 boundary
		 *
		 * @param boundary boundary
		 * @return this
		 */
		public EffectiveHttp.FormUtil setBoundary(String boundary) {
			builder.setBoundary(boundary);
			return this;
		}

		/**
		 * 执行请求
		 *
		 * @return 封装返回参数
		 */
		public EffectiveHttp.ResponseWrap execute() {
			HttpEntity httpEntity = builder.build();
			httpPost.setEntity(httpEntity);
			return exec(httpPost);
		}
	}

	/**
	 * 响应包装
	 */
	public static class ResponseWrap {
		private CloseableHttpResponse response;
		private HttpEntity entity;

		ResponseWrap(CloseableHttpResponse response) {
			this.response = response;

			try {
				HttpEntity entity = response.getEntity();
				if (null != entity) {
					this.entity = new BufferedHttpEntity(entity);
				} else {
					this.entity = new BasicHttpEntity();
				}
				//释放连接
				EntityUtils.consume(entity);
				this.response.close();
			} catch (IOException e) {
				logger.error(e.getMessage(), e);
			}
		}

		/**
		 * 获取响应文本信息
		 *
		 * @return
		 */
		public String getString() {
			try {
				return EntityUtils.toString(entity, Consts.UTF_8);
			} catch (IOException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}

		public byte[] getBytes() {
			try {
				return EntityUtils.toByteArray(entity);
			} catch (ParseException | IOException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}

		/**
		 * 获取Http响应码
		 *
		 * @return
		 */
		public int statusCode() {
			return response.getStatusLine().getStatusCode();
		}

		/**
		 * 获取InputStream
		 *
		 * @return
		 */
		public InputStream getInputStream() {
			try {
				return entity.getContent();
			} catch (IOException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}

		/**
		 * 获取Response
		 */
		public CloseableHttpResponse getResponse() {
			return this.response;
		}

	}
}

说明:

(1)http连接池策略适用于高并发访问同一主机(或同一个接口)的情况下。

(2)实际运用于项目进行jmeter压测,tps等能够显著提升.

(3)本文只是简单举例说明思想,工具扩展可以根据个人喜好重写。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值