带有连接池的Http客户端工具类HttpClientUtil

一、背景

业务开发中,经常会遇到通过http/https向下游服务发送请求。每次都要重复造轮子写HttpClient的逻辑,而且性能、功能参差不齐。这里分享一个高性能的、带连接池的通用Http客户端工具。

请尊重作者劳动成果,转载请标明原文链接:https://www.cnblogs.com/waterystone/p/11551280.html

二、特点

  • 基于apache的高性能Http客户端org.apache.http.client.HttpClient;
  • 连接池的最大连接数默认是20,可通过系统变量-Dadu.common.http.max.total=200指定;
  • 连接池的每个路由的最大连接数默认是2,可通过系统变量-Dadu.common.http.max.per.route=10指定;
  • 可设置超时,通过HttpOptions进行设置;
  • 可重试,通过HttpOptions进行设置。

三、源码

参考:https://github.com/waterystone/adu-test/blob/master/src/main/java/com/adu/utils/HttpClientUtil.java

  1 package com.adu.utils;
  2 
  3 import com.adu.Constants;
  4 import com.adu.handler.HttpRequestRetryHandler;
  5 import com.adu.model.HttpOptions;
  6 import org.apache.http.HttpEntity;
  7 import org.apache.http.NameValuePair;
  8 import org.apache.http.client.config.RequestConfig;
  9 import org.apache.http.client.entity.UrlEncodedFormEntity;
 10 import org.apache.http.client.methods.CloseableHttpResponse;
 11 import org.apache.http.client.methods.HttpGet;
 12 import org.apache.http.client.methods.HttpPost;
 13 import org.apache.http.client.methods.HttpRequestBase;
 14 import org.apache.http.client.utils.URIBuilder;
 15 import org.apache.http.client.utils.URLEncodedUtils;
 16 import org.apache.http.config.Registry;
 17 import org.apache.http.config.RegistryBuilder;
 18 import org.apache.http.conn.socket.ConnectionSocketFactory;
 19 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
 20 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
 21 import org.apache.http.entity.StringEntity;
 22 import org.apache.http.impl.client.CloseableHttpClient;
 23 import org.apache.http.impl.client.HttpClients;
 24 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 25 import org.apache.http.message.BasicNameValuePair;
 26 import org.apache.http.util.EntityUtils;
 27 import org.slf4j.Logger;
 28 import org.slf4j.LoggerFactory;
 29 
 30 import javax.net.ssl.SSLContext;
 31 import java.nio.charset.StandardCharsets;
 32 import java.util.ArrayList;
 33 import java.util.List;
 34 import java.util.Map;
 35 import java.util.Objects;
 36 import java.util.stream.Collectors;
 37 
 38 /**
 39  * 带有连接池的Http客户端工具类。具有如下特点:
 40  * <ol>
 41  * <li>基于apache的高性能Http客户端{@link org.apache.http.client.HttpClient};</li>
 42  * <li>连接池的最大连接数默认是20,可通过系统变量-Dzzarch.common.http.max.total=200指定;</li>
 43  * <li>连接池的每个路由的最大连接数默认是2,可通过系统变量-Dzzarch.common.http.max.per.route=10指定;</li>
 44  * <li>可设置超时,通过{@link HttpOptions}进行设置;</li>
 45  * <li>可重试,通过{@link HttpOptions}进行设置;</li>
 46  * </ol>
 47  *
 48  * @author duyunjie
 49  * @date 2019-09-18 16:33
 50  */
 51 public class HttpClientUtil {
 52     private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
 53 
 54     /**
 55      * HttpClient 连接池
 56      */
 57     private static PoolingHttpClientConnectionManager CONNECTION_MANAGER = initPoolingHttpClientConnectionManager();
 58 
 59     public static String httpGet(String url) throws Exception {
 60         return httpGet(url, null, null, null);
 61     }
 62 
 63 
 64     public static String httpGet(String url, HttpOptions httpOptions) throws Exception {
 65         return httpGet(url, null, null, httpOptions);
 66     }
 67 
 68 
 69     public static String httpGet(String url, Map<String, ?> params) throws Exception {
 70         return httpGet(url, null, params, null);
 71     }
 72 
 73 
 74     public static String httpGet(String url, Map<String, ?> params, HttpOptions httpOptions) throws Exception {
 75         return httpGet(url, null, params, httpOptions);
 76     }
 77 
 78 
 79     public static String httpGet(String url, Map<String, ?> headers, Map<String, ?> params) throws Exception {
 80         return httpGet(url, headers, params, null);
 81     }
 82 
 83     /**
 84      * 发送 HTTP GET请求
 85      *
 86      * @param url
 87      * @param headers     请求头
 88      * @param params      请求参数
 89      * @param httpOptions 配置参数,如重试次数、超时时间等。
 90      * @return
 91      * @throws Exception
 92      */
 93     public static String httpGet(String url, Map<String, ?> headers, Map<String, ?> params, HttpOptions httpOptions) throws Exception {
 94         // 装载请求地址和参数
 95         URIBuilder ub = new URIBuilder();
 96         ub.setPath(url);
 97 
 98         // 转换请求参数
 99         List<NameValuePair> pairs = convertParams2NVPS(params);
100         if (!pairs.isEmpty()) {
101             ub.setParameters(pairs);
102         }
103         HttpGet httpGet = new HttpGet(ub.build());
104 
105         // 设置请求头
106         if (Objects.nonNull(headers)) {
107             for (Map.Entry<String, ?> param : headers.entrySet()) {
108                 httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
109             }
110         }
111 
112         return doHttp(httpGet, httpOptions);
113     }
114 
115 
116     public static String httpPost(String url, Map<String, ?> params) throws Exception {
117         return httpPost(url, null, params, null);
118     }
119 
120     public static String httpPost(String url, Map<String, ?> params, HttpOptions httpOptions) throws Exception {
121         return httpPost(url, null, params, httpOptions);
122     }
123 
124 
125     public static String httpPost(String url, Map<String, ?> headers, Map<String, ?> params) throws Exception {
126         return httpPost(url, headers, params, null);
127     }
128 
129     /**
130      * 发送 HTTP POST请求
131      *
132      * @param url
133      * @param headers     请求头
134      * @param params      请求参数
135      * @param httpOptions 配置参数,如重试次数、超时时间等。
136      * @return
137      * @throws Exception
138      */
139     public static String httpPost(String url, Map<String, ?> headers, Map<String, ?> params, HttpOptions httpOptions) throws Exception {
140         HttpPost httpPost = new HttpPost(url);
141 
142         // 转换请求参数
143         List<NameValuePair> pairs = convertParams2NVPS(params);
144         if (!pairs.isEmpty()) {
145             httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));
146         }
147 
148         // 设置请求头
149         if (Objects.nonNull(headers)) {
150             for (Map.Entry<String, ?> param : headers.entrySet()) {
151                 httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
152             }
153         }
154 
155         return doHttp(httpPost, httpOptions);
156     }
157 
158 
159     /**
160      * 发送 HTTP POST请求,参数格式JSON
161      * <p>请求参数是JSON格式,数据编码是UTF-8</p>
162      *
163      * @param url
164      * @param param
165      * @return
166      * @throws Exception
167      */
168     public static String httpPostJson(String url, String param, HttpOptions httpOptions) throws Exception {
169         HttpPost httpPost = new HttpPost(url);
170 
171         // 设置请求头
172         httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
173 
174         // 设置请求参数
175         httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
176 
177         return doHttp(httpPost, httpOptions);
178     }
179 
180     /**
181      * 发送 HTTP POST请求,参数格式XML
182      * <p>请求参数是XML格式,数据编码是UTF-8</p>
183      *
184      * @param url
185      * @param param
186      * @return
187      * @throws Exception
188      */
189     public static String httpPostXml(String url, String param, HttpOptions httpOptions) throws Exception {
190         HttpPost httpPost = new HttpPost(url);
191 
192         // 设置请求头
193         httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8");
194 
195         // 设置请求参数
196         httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
197 
198         return doHttp(httpPost, httpOptions);
199     }
200 
201 
202     /**
203      * 转换请求参数,将Map键值对拼接成QueryString字符串
204      *
205      * @param params
206      * @return
207      */
208     public static String convertParams2QueryStr(Map<String, ?> params) {
209         List<NameValuePair> pairs = convertParams2NVPS(params);
210 
211         return URLEncodedUtils.format(pairs, StandardCharsets.UTF_8.name());
212     }
213 
214     /**
215      * 转换请求参数
216      *
217      * @param params
218      * @return
219      */
220     public static List<NameValuePair> convertParams2NVPS(Map<String, ?> params) {
221         if (params == null) {
222             return new ArrayList<>();
223         }
224 
225         return params.entrySet().stream().map(param -> new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue()))).collect(Collectors.toList());
226     }
227 
228     /**
229      * 发送 HTTP 请求
230      *
231      * @param request
232      * @return
233      * @throws Exception
234      */
235     private static String doHttp(HttpRequestBase request, HttpOptions httpOptions) throws Exception {
236         if (Objects.isNull(httpOptions)) {//如果为空,则用默认的。
237             httpOptions = new HttpOptions();
238         }
239         // 设置超时时间
240         if (Objects.nonNull(httpOptions.getTimeoutMs())) {
241             request.setConfig(RequestConfig.custom().setSocketTimeout(httpOptions.getTimeoutMs()).build());
242         }
243 
244         //设置重试策略
245         HttpRequestRetryHandler httpRequestRetryHandler = null;
246         if (Objects.nonNull(httpOptions.getRetryCount())) {
247             httpRequestRetryHandler = new HttpRequestRetryHandler(httpOptions.getRetryCount());
248         }
249 
250         // 通过连接池获取连接对象
251         CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(CONNECTION_MANAGER).setRetryHandler(httpRequestRetryHandler).build();
252         return doRequest(httpClient, request);
253 
254     }
255 
256     /**
257      * 处理Http/Https请求,并返回请求结果
258      * <p>注:默认请求编码方式 UTF-8</p>
259      *
260      * @param httpClient
261      * @param request
262      * @return
263      * @throws Exception
264      */
265     private static String doRequest(CloseableHttpClient httpClient, HttpRequestBase request) throws Exception {
266         String result = null;
267         CloseableHttpResponse response = null;
268 
269         try {
270             // 获取请求结果
271             response = httpClient.execute(request);
272             // 解析请求结果
273             HttpEntity entity = response.getEntity();
274             // 转换结果
275             result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
276             // 关闭IO流
277             EntityUtils.consume(entity);
278         } finally {
279             if (null != response) {
280                 response.close();
281             }
282         }
283 
284         return result;
285     }
286 
287 
288     /**
289      * 初始化连接池
290      *
291      * @return
292      */
293     private static PoolingHttpClientConnectionManager initPoolingHttpClientConnectionManager() {
294         // 初始化连接池,可用于请求HTTP/HTTPS(信任所有证书)
295         PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(getRegistry());
296 
297         // 整个连接池的最大连接数
298         String maxTotal = System.getProperty(Constants.SYSTEM_PROPERTY_KEY_HTTP_MAX_TOTAL);
299         if (Objects.nonNull(maxTotal)) {
300             connectionManager.setMaxTotal(Integer.valueOf(maxTotal));
301         }
302 
303         // 每个路由的最大连接数
304         String maxPerRoute = System.getProperty(Constants.SYSTEM_PROPERTY_KEY_HTTP_MAX_PER_ROUTE);
305         if (Objects.nonNull(maxPerRoute)) {
306             connectionManager.setDefaultMaxPerRoute(Integer.valueOf(maxPerRoute));
307         }
308 
309         logger.info("[ZZARCH_COMMON_SUCCESS_initPoolingHttpClientConnectionManager]maxTotal={},maxPerRoute={}", maxTotal, maxPerRoute);
310         return connectionManager;
311     }
312 
313 
314     /**
315      * 获取 HTTPClient注册器
316      *
317      * @return
318      * @throws Exception
319      */
320     private static Registry<ConnectionSocketFactory> getRegistry() {
321         try {
322             return RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", new SSLConnectionSocketFactory(SSLContext.getDefault())).build();
323         } catch (Exception e) {
324             logger.error("[ERROR_getRegistry]", e);
325         }
326 
327         return null;
328     }
329 
330 }

 

 

end

 

转载于:https://www.cnblogs.com/waterystone/p/11551280.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Http连接池工具类是一个用于管理和复用Http连接的工具类。随着互联网的发展和使用场景的增多,使用Http发送请求的需求也越来越多,而每次发送请求都创建一个新的Http连接会造成资源的浪费和效率低下。 Http连接池工具类的作用就是在应用程序初始化时,创建一定数量的Http连接,然后将这些连接保存在一个连接池中,当需要发送请求时,直接从连接池中获取一个可用的连接来发送请求,请求完成后将连接放回连接池中。 这样做的好处是可以减少每次发送请求时创建和销毁连接的开销,提高请求的效率。另外,由于连接是复用的,连接池可以控制连接的数量,避免创建过多的连接导致资源的浪费。并且,连接池还可以对连接进行管理和监控,例如可以设置连接的超时时间,当连接超过一定时间没有使用时,连接池可以自动将其关闭并移除。同时,连接池还可以实现一些高级功能,例如连接的自动重试和故障恢复等。 在使用Http连接池工具类时,需要先初始化连接池,指定最大连接数、每个连接的最大请求次数、连接的最大空闲时间等参数。然后,在发送请求时,只需要从连接池中获取一个连接,使用完毕后将连接放回连接池即可。 总之,Http连接池工具类可以在Http请求频繁的场景下提高请求的效率,减少资源的浪费,并提供一些高级功能,是一个非常实用的工具类

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值