HttpClient4.5X使用-集成微服务

      1.什么是HttpClient

HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。现在HttpClient最新版本为 HttpClient 4.5 .6(官网)

        实际应用中我们通常使用在复杂soap协议类型的调用。Springboot一般推荐有RestTemplate 。  

2. 整合 HttpClient

1.配置类,主要配置httpClient 连接池的信息

@ConfigurationProperties(prefix = HttpClientProperties.PREFIX)
public class HttpClientProperties {
public static final String PREFIX = "http.config"; //默认连接池最大连接数
private Integer httpMaxTotal = 300; //默认整个连接池默认最大连接数
private Integer httpDefaultMaxPerRoute = 100; //设置请求超时 默认30s
private Integer httpConnectTimeout = 30 * 1000; //设置请求响应超时时间 默认5min
private Integer getHttpReadTimeout = 5 * 60 * 1000; //设置从连接池获取中获取到连接最长时间
private Integer httpConnectionRequestTimeout = 500; //设置数据传输的最长时间
private Integer httpSocketTimeout = 5 * 60 * 1000; //设置默认请求类型http 、https
private String hostType ="http"; public Integer getHttpMaxTotal() {
return httpMaxTotal;
} public void setHttpMaxTotal(Integer httpMaxTotal) {
this.httpMaxTotal = httpMaxTotal;
} public Integer getHttpDefaultMaxPerRoute() {
return httpDefaultMaxPerRoute;
} public void setHttpDefaultMaxPerRoute(Integer httpDefaultMaxPerRoute) {
this.httpDefaultMaxPerRoute = httpDefaultMaxPerRoute;
} public Integer getHttpConnectTimeout() {
return httpConnectTimeout;
} public void setHttpConnectTimeout(Integer httpConnectTimeout) {
this.httpConnectTimeout = httpConnectTimeout;
} public Integer getGetHttpReadTimeout() {
return getHttpReadTimeout;
} public void setGetHttpReadTimeout(Integer getHttpReadTimeout) {
this.getHttpReadTimeout = getHttpReadTimeout;
} public Integer getHttpConnectionRequestTimeout() {
return httpConnectionRequestTimeout;
} public void setHttpConnectionRequestTimeout(Integer httpConnectionRequestTimeout) {
this.httpConnectionRequestTimeout = httpConnectionRequestTimeout;
} public Integer getHttpSocketTimeout() {
return httpSocketTimeout;
} public void setHttpSocketTimeout(Integer httpSocketTimeout) {
this.httpSocketTimeout = httpSocketTimeout;
} public String getHostType() {
return hostType;
} public void setHostType(String hostType) {
this.hostType = hostType;
}
}

      2.初始化连接池信息

@Configuration
@ConditionalOnClass(HttpClientConfig.class)
@EnableConfigurationProperties(HttpClientProperties.class)
public class HttpClientConfig { @Autowired
HttpClientProperties httpClientProperties;
@Autowired
private PoolingHttpClientConnectionManager manager; @Bean
public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// 最大连接数
poolingHttpClientConnectionManager.setMaxTotal(httpClientProperties.getHttpMaxTotal());
// 每个主机的最大并发数
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpClientProperties.getHttpDefaultMaxPerRoute());
return poolingHttpClientConnectionManager;
} @Bean
public IdleConnectionEvictor idleConnectionEvictor(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager ) {
// 定期清理无效连接
return new IdleConnectionEvictor(poolingHttpClientConnectionManager, 1L, TimeUnit.HOURS);
} @Bean
public RequestConfig requestConfig() {
// 请求配置
// 创建连接的最长时间
return RequestConfig.custom().setConnectTimeout(httpClientProperties.getHttpConnectTimeout())
// 从连接池中获取到连接的最长时间
.setConnectionRequestTimeout(httpClientProperties.getHttpConnectTimeout())
// 数据传输的最长时间
.setSocketTimeout(httpClientProperties.getHttpSocketTimeout())
.build();
} /**
* 通过httpClients直接获取
* @param requestConfig 配置信息
* @return
*/
@Bean
@Scope("prototype") //此处是多例模式,因为考虑连接池使用都会手动关闭,同时单例会存在未知问题
public CloseableHttpClient closeableHttpClient(RequestConfig requestConfig) {
return HttpClients.custom().setConnectionManager(this.manager).setDefaultRequestConfig(requestConfig).build();
} /**
* 通过HttpClientBuilder 间接获取
* @param requestConfig 配置信息
* @return
*/
@Bean
public HttpClientBuilder httpClientBuilder(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager, RequestConfig requestConfig){
return HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager).setDefaultRequestConfig(requestConfig);
} }

3.httpclientUtil

@Component
public class HttpClientUtil { @Autowired
private CloseableHttpClient httpClient; /**
* get请求用于普通接口调用
*
* @param url 请求地址
* @param headers 请求头
* @param params 参数
* @return
* @throws Exception
*/
public String doGet(String url,
Map<String, String> headers,
Map<String, String> params) {
// //创建一个默认可关闭的httpclient对象
// CloseableHttpClient httpClient = HttpClients.createDefault();
//定义httpResponse 对象
CloseableHttpResponse response = null;
//反馈信息
String resultMsg = null;
try {
//设置请求地址
URIBuilder builder = new URIBuilder(url);
//拼接地址
if (!CollectionUtils.isEmpty(params)) {
params.forEach((k, v) -> {
builder.addParameter(k, v);
});
}
//设置请求头
HttpGet httpGet = new HttpGet(builder.build());
if (!CollectionUtils.isEmpty(headers)) {
headers.entrySet().stream().map(e -> {
httpGet.addHeader(e.getKey(), e.getValue());
return httpGet;
});
}
// 执行请求
response = httpClient.execute(httpGet);
// 判断返回状态为200则给返回值赋值
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//构造反馈
resultMsg = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
//构造反馈SRM
e.printStackTrace();
} finally {
//连接池关闭
httpClientCloseResponse(httpClient, response);
}
return resultMsg;
} /**
* 无请求体post请求 post form
*
* @param url 请求地址
* @param headers 请求头
* @param params 参数
* @return
* @throws Exception
*/
public String doPost(String url,
Map<String, String> headers,
Map<String, String> params) {
//创建一个默认可关闭的httpclient对象
// CloseableHttpClient httpClient = HttpClients.createDefault();
//定义httpResponse 对象
CloseableHttpResponse response = null;
//反馈信息
String resultMsg = null;
try {
//设置请求地址
URIBuilder builder = new URIBuilder(url);
//拼接地址
if (!CollectionUtils.isEmpty(params)) {
params.forEach((k, v) -> {
builder.addParameter(k, v);
});
}
//设置请求头
HttpPost httpPost = new HttpPost(builder.build());
if (!CollectionUtils.isEmpty(headers)) {
headers.forEach((k, v) -> {
httpPost.addHeader(k, v);
});
}
// 执行请求
response = httpClient.execute(httpPost);
// 判断返回状态为200则给返回值赋值
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//构造反馈
resultMsg = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
//构造反馈SRM
e.printStackTrace();
} finally {
//连接池关闭
httpClientCloseResponse(httpClient, response);
}
return resultMsg;
} /**
* 请求体post请求 post
*
* @param url 请求地址
* @param headers 请求头
* @param params 参数信息
* @param body 请求体
* @return 反馈结果
* @throws Exception
*/
public String doPost(String url,
Map<String, String> headers,
Map<String, String> params,
String body) {
//创建一个默认可关闭的httpclient对象
// CloseableHttpClient httpClient = HttpClients.createDefault();
//定义httpResponse 对象
CloseableHttpResponse response = null;
//反馈信息
String resultMsg = null;
try {
//设置请求地址
URIBuilder builder = new URIBuilder(url);
//拼接地址
if (!CollectionUtils.isEmpty(params)) {
List<NameValuePair> nameValuePairs = params.entrySet().stream().map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())).collect(Collectors.toList());
builder.setParameters(nameValuePairs);
}
//设置请求头
HttpPost httpPost = new HttpPost(builder.build());
if (!CollectionUtils.isEmpty(headers)) {
headers.forEach((k, v) -> {
httpPost.addHeader(k, v);
});
}
//设置请求体
if (StringUtils.isNotBlank(body)) {
httpPost.setEntity(new StringEntity(body, "utf-8"));
}
// 执行请求
response = httpClient.execute(httpPost);
// 判断返回状态为200则给返回值赋值
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//构造反馈
resultMsg = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
//构造反馈SRM
e.printStackTrace();
} finally {
//连接池关闭
httpClientCloseResponse(httpClient, response);
}
return resultMsg; } /**
* 构建请求的 url
*
* @param uri
* @param querys
* @return
* @throws UnsupportedEncodingException
*/
private String buildUrl(String uri, Map<String, String> querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder();
sbUrl.append(uri);
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry<String, String> query : querys.entrySet()) {
if (0 < sbQuery.length()) {
sbQuery.append("&");
}
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}
if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
}
}
}
if (0 < sbQuery.length()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
} /**
* 将结果转换成JSONObject
*
* @param httpResponse
* @return
* @throws IOException
*/
public JSONObject getJson(HttpResponse httpResponse) throws IOException {
HttpEntity entity = httpResponse.getEntity();
String resp = EntityUtils.toString(entity, "UTF-8");
EntityUtils.consume(entity);
return JSON.parseObject(resp);
} /**
* 关闭连接池反馈
*
* @param httpResponse
*/
public void httpClientCloseResponse(CloseableHttpClient httpClient, CloseableHttpResponse httpResponse) {
try {
if (httpResponse != null) {
httpResponse.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
} }

  3.为什么使用HttpClient并且使用连接池

           日常调用时候我们经常碰见这样的问题:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
at sun.security.ssl.Handshaker.processLoop(Unknown Source)
at sun.security.ssl.Handshaker.process_record(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(Unknown Source)
at com.hx.net.Test3.main(Test3.java:21)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
... 12 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(Unknown Source)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 18 more

  证书认证问题SSL,网上处理方式参差不齐。因此通过 查看PoolingHttpClientConnectionManager实例化源码我们发现

 private static Registry<ConnectionSocketFactory> getDefaultRegistry() {
return RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
} public PoolingHttpClientConnectionManager() {
this(getDefaultRegistry());
}

  httpclient 连接池已经对ssl证书初始化注册是有非常良好的集成。

 

HttpClient4.5X使用-集成微服务的更多相关文章

  1. 开源:Taurus.MVC-Java 版本框架 (支持javax.servlet.*和jakarta.servlet.*双系列,内集成微服务客户端)

    版本说明: 因为之前有了Taurus.MVC-DotNet 版本框架,因此框架标了-Java后缀. .Net  版本: 开源文章:开源:Taurus.MVC-DotNet 版本框架 (支持.NET C ...

  2. Taurus.MVC 微服务框架 入门开发教程:项目集成:2、客户端:ASP.NET Core(C#)项目集成:应用中心。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  3. Taurus.MVC 微服务框架 入门开发教程:项目集成:3、客户端:其它编程语言项目集成:Java集成应用中心。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  4. Chris Richardson微服务翻译:重构单体服务为微服务

    Chris Richardson 微服务系列翻译全7篇链接: 微服务介绍 构建微服务之使用API网关 构建微服务之微服务架构的进程通讯 微服务架构中的服务发现 微服务之事件驱动的数据管理 微服务部署 ...

  5. 微服务和SpringCloud入门

    微服务和SpringCloud入门 微服务是什么 微服务的核心是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底去耦合,每个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种 ...

  6. 推荐一款 Python 微服务框架 - Nameko

    1. 前言 大家好,我是安果! 考虑到 Python 性能及效率性,Python Web 端一直不温不火,JAVA 和 Golang 的微服务生态一直很繁荣,也被广泛用于企业级应用开发当中 ​本篇文章 ...

  7. 架构师成长之路也该了解的新一代微服务技术-ServiceMesh(上)

    架构演进 发展历程 我们再来回顾一下架构发展历程,从前往后的顺序依次为单机小型机->垂直拆分->集群化负载均衡->服务化改造架构->服务治理->微服务时代 单机小型机:采 ...

  8. NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成

    本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博文了,最近忙着两件事;    一:阅读刘墉先生的<说话的魅力>,以一种微妙的,你我大家都会经常遇见 ...

  9. 【下一代核心技术DevOps】:(五)微服务CI与Rancher持续集成

    1. 引言 DevOps的核心魅力是快速的持续集成交付,降低研发和实施运维之间的交互,使得传统的各种扯皮现象统统消失.最重要的是降低成本 保障产品交付可靠性. 使用Rancher作为持续集成的关键环节 ...

随机推荐

  1. 【纯水题】POJ 1852 Ants

    题目大意 有一根长\(L\)厘米米的水平木棍上有\(n\)个蚂蚁,它们以每秒1cm/s的爬(fei)行(ben)到木棍的一端,之后掉下去. 给出每个蚂蚁的起始位置,但是不知道它们爬行的方向.相向而行的 ...

  2. 【事件中心 Azure Event Hub】使用Logstash消费EventHub中的event时遇见的几种异常(TimeoutException, ReceiverDisconnectedException)

    问题描述 使用EFK(Elasticsearch, Fluentd and Kibana)在收集日志的解决方案中, 可以先把日志发送到EventHub中,然后通过Logstash消费EventHub中 ...

  3. Pytest配置文件声明自定义用例标识

    使用pytest.ini添加自定义用例标识: [pytest] # 1.使用没有注册过的标记抛出错误 addopts = --strict-markers # 2.自定义标记 markers = sm ...

  4. hystrix线程池隔离的原理与验证

    引子 幸福很简单: 今天项目半年规划被通过,终于可以早点下班.先坐公交,全程开着灯,买了了几天的书竟然有时间看了.半小时后,公交到站,换乘大巴车.车还等着上人的功夫,有昏暗的灯光,可以继续看会儿书.过 ...

  5. JVM内存管理和垃圾回收

    无论对于Java程序员还是大数据研发人员,JVM是必须掌握的技能之一.既是面试中经常问的问题,也是在实际业务中对程序进行调优.排查类似于内存溢出.栈溢出.内存泄漏等问题的关键.笔者将按下图分多篇文章详 ...

  6. Docker容器和K8s添加Health Check

    docker容器启动后,怎么确认容器运行正常,怎么确认可以对外提供服务了,这就需要health check功能了. 之前对health check的功能不在意,因为只要镜像跑起来了就是健康的,如果有问 ...

  7. git每次提交代码都要设置账号密码的问题

    git config --global credential.helper store 待下一次提交代码的时候,输入了正确的用户名和密码,之后 就不需要输入用户名密码

  8. MATLAB中的参数估计函数详解及调用示例【联合整理】

    前言 因为最近项目上的需要,才发现MATLAB的统计工具箱中的参数估计函数,觉得很简单很好用,现在把所有的参数估计函数整理一下,并在最后面附上调用示例. 参与人员 由于时间关系,这篇随笔是两个人一起整 ...

  9. Redis常用命令(4)——List

    BLPOP 格式:BLPOP key [key ...] timeout 作用:从列表左侧弹出元素,超时时间为timeout,单位为秒.如果timeout为0则一直阻塞直到列表中有元素.如果同时操作多 ...

  10. Learn day3 深浅拷贝/格式化/字符串/列表/字典/集合/文件操作

    1. pass break continue # ### pass break continue # (1) pass 过 """如果代码块当中,什么也不写,用pass来 ...