本文为博主原创,转载请注明出处:

  项目中存在第三方系统之间的服务调用通信,且会进行频繁调用,由于很早之前实现的调用方式为每调用一次外部接口,就需要新建一个HttpClient 对象。由于频繁调用,会存在性能问题。

针对这种场景,进行优化,使用httpClient 连接池,避免重复频繁创建httpClient 造成性能问题。以下为简单实现的demo:

  1. 对 httpClient 的属性及常用配置封装 HttpPoolProperties

package com.example.demo.config;

import lombok.Data;
import org.springframework.stereotype.Component; @Component
//@ConfigurationProperties(prefix = "http.pool.conn") // 可在配置文件中进行配置
@Data
public class HttpPoolProperties {
// 最大连接数
private Integer maxTotal = 20;
// 同路由并发数
private Integer defaultMaxPerRoute =20 ;
private Integer connectTimeout = 2000;
private Integer connectionRequestTimeout=2000;
private Integer socketTimeout= 2000;
// 线程空闲多久后进行校验
private Integer validateAfterInactivity= 2000;
// 重试次数
private Integer retryTimes = 2; // 是否开启充实
private boolean enableRetry = true;
// 重试的间隔:可实现 ServiceUnavailableRetryStrategy 接口
private Integer retryInterval= 2000;
}

2.  创建httpClient 连接池,并对RestTemplate 指定httpClient 及连接池

  

package com.example.demo.util;

import com.example.demo.config.HttpPoolProperties;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
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.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate; @Configuration
public class HttpClientPoolUtils { @Autowired
private HttpPoolProperties httpPoolProperties; /**
* 首先实例化一个连接池管理器,设置最大连接数、并发连接数
* @return
*/
@Bean(name = "httpClientConnectionManager")
public PoolingHttpClientConnectionManager getHttpClientConnectionManager(){
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build(); PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);
//最大连接数
httpClientConnectionManager.setMaxTotal(httpPoolProperties.getMaxTotal());
//并发数
httpClientConnectionManager.setDefaultMaxPerRoute(httpPoolProperties.getDefaultMaxPerRoute()); httpClientConnectionManager.setValidateAfterInactivity(httpPoolProperties.getValidateAfterInactivity()); return httpClientConnectionManager;
} /**
* 实例化连接池,设置连接池管理器。
* 这里需要以参数形式注入上面实例化的连接池管理器
* @param httpClientConnectionManager
* @return
*/
@Bean(name = "httpClientBuilder")
public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager")PoolingHttpClientConnectionManager httpClientConnectionManager){ //HttpClientBuilder中的构造方法被protected修饰,所以这里不能直接使用new来实例化一个HttpClientBuilder,可以使用HttpClientBuilder提供的静态方法create()来获取HttpClientBuilder对象
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); httpClientBuilder.setConnectionManager(httpClientConnectionManager); if (httpPoolProperties.isEnableRetry()){
// 重试次数
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(httpPoolProperties.getRetryTimes(), true));
// 若需要自定义http 的重试策略,可以重新实现ServiceUnavailableRetryStrategy 或 HttpRequestRetryHandler接口,比如对指定异常或制定状态码进行重试,并指定充实的次数。
}else {
httpClientBuilder.disableAutomaticRetries();
}
// 另外httpClientBuilder 可以设置长连接策略,dns解析器,代理,拦截器以及UserAgent等等。可根据业务需要进行实现 return httpClientBuilder;
} /* 注入连接池,用于获取httpClient
* @param httpClientBuilder
* @return
*/
@Bean("httpClient")
public CloseableHttpClient httpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){
return httpClientBuilder.build();
} /**
* Builder是RequestConfig的一个内部类
* 通过RequestConfig的custom方法来获取到一个Builder对象
* 设置builder的连接信息
* 这里还可以设置proxy,cookieSpec等属性。有需要的话可以在此设置
* @return
*/
@Bean(name = "builder")
public RequestConfig.Builder getBuilder(){
RequestConfig.Builder builder = RequestConfig.custom();
return builder.setConnectTimeout(httpPoolProperties.getConnectTimeout()) //连接上服务器(握手成功)的时间,超出抛出connect timeout
//从连接池中获取连接的超时时间,超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
.setConnectionRequestTimeout(httpPoolProperties.getConnectionRequestTimeout())
//服务器返回数据(response)的时间,超过抛出read timeout
.setSocketTimeout(httpPoolProperties.getSocketTimeout());
} /**
* 使用builder构建一个RequestConfig对象
* @param builder
* @return
*/
@Bean
public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){
return builder.build();
} /**
* RestTemplate 指定httpClient 及连接池
*
* @param httpClient
* @return
*/
@Bean(name = "httpClientTemplate")
public RestTemplate restTemplate(@Qualifier("httpClient") CloseableHttpClient httpClient) {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(factory);
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
return restTemplate;
} }

  3。 创建清理线程对httpClient 空闲线程,失效线程进行清理

package com.example.demo.util;

import org.apache.http.conn.HttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class IdleConnectionEvictor extends Thread {
@Autowired
private HttpClientConnectionManager connMgr; private volatile boolean shutdown; public IdleConnectionEvictor() {
super();
super.start();
} @Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// 关闭失效的连接
connMgr.closeExpiredConnections();
}
}
} catch (InterruptedException ex) {
// 结束
}
} //关闭清理无效连接的线程
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}

  

  4. 单元测试

package com.example.demo;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.client.RestTemplate; import java.io.IOException; @Slf4j
@SpringBootTest
public class HttpTest { @Autowired
private RestTemplate httpClientTemplate; @Autowired
private CloseableHttpClient httpClient; @Test
void test() throws IOException {

String result = httpClientTemplate.getForObject("https://www.baidu.com/",String.class);
System.out.println("httpClientTemplate==="+result);

// 声明 http get 请求
String url = "https://www.baidu.com/";
HttpGet httpGet = new HttpGet(url);
// 发起请求
CloseableHttpResponse response = this.httpClient.execute(httpGet);
System.out.println("httpClient==="+response);
} }

  测试方法分别通过CloseableHttpClient   httpClient  进行http调用与自定义的RestTemplate httpClientTemplate 进行 http 调用。

   执行结果如下:

http连接池配置及spring boot restTemplate配置http连接池的更多相关文章

  1. Spring boot --- 自动配置

    spring boot 自动配置 指的是针对很多spring 应用程序常见的应用功能,spring boot 能自动提供相关配置. spring boot 自动配置加载     Spring boot ...

  2. Spring Boot Security配置教程

    1.简介 在本文中,我们将了解Spring Boot对spring Security的支持. 简而言之,我们将专注于默认Security配置以及如何在需要时禁用或自定义它. 2.默认Security设 ...

  3. Spring Boot自定义配置与加载

    Spring Boot自定义配置与加载 application.properties主要用来配置数据库连接.日志相关配置等.除了这些配置内容之外,还可以自定义一些配置项,如: my.config.ms ...

  4. Spring Boot常用配置

    概述 本文主要写了下Spring Boot的一些常用配置. Spring Boot基本配置 入口类: Spring Boot通常有一个名为*Application的入口类,入口类里面有一个main方法 ...

  5. Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件

    本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...

  6. 在 Spring Boot 中使用 HikariCP 连接池

    上次帮小王解决了如何在 Spring Boot 中使用 JDBC 连接 MySQL 后,我就一直在等,等他问我第三个问题,比如说如何在 Spring Boot 中使用 HikariCP 连接池.但我等 ...

  7. Spring Boot + Mybatis 配置多数据源

    Spring Boot + Mybatis 配置多数据源 Mybatis拦截器,字段名大写转小写 package com.sgcc.tysj.s.common.mybatis; import java ...

  8. spring boot 环境配置(profile)切换

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  9. Spring Boot自动配置原理

    使用Spring Boot之后,一个整合了SpringMVC的WEB工程开发,变的无比简单,那些繁杂的配置都消失不见了,这 是如何做到的? 一切魔力的开始,都是从我们的main函数来的,所以我们再次来 ...

  10. Sping Boot入门到实战之入门篇(四):Spring Boot自动化配置

    该篇为Sping Boot入门到实战系列入门篇的第四篇.介绍Spring Boot自动化配置的基本原理与实现.   Spring Boot之所以受开发者欢迎, 其中最重要的一个因素就是其自动化配置特性 ...

随机推荐

  1. linux 上安装kapacitor

    转载请注明出处: 1.通过安装包安装 1.使用以下命令下载安装包: wget https://dl.influxdata.com/kapacitor/releases/kapacitor_1.6.3- ...

  2. spring是否线程安全

    spring 管理的bean默认是单例的,可通过 scope 属性设置scope="singleton" 默认是单例,可修改为scope="prototype" ...

  3. HTML之元素相对页面(视口)左上角的绝对坐标

    目录 一. 绝对位置 二. 相对位置 一. 绝对位置 网页元素的绝对位置,指该元素的左上角相对于整张网页左上角的坐标.无论网页滚动条如何滚动,它都是不会变化的. // 获取元素的绝对位置坐标(相对于页 ...

  4. Ubuntu 终端如何分割多个窗口

    sudo apt-get install terminator 查看 ~/.config(隐藏文件夹 ctrl + h 即可看见) 下是否有 terminator 文件夹 如果没有手动创建一个 然后在 ...

  5. 目标检测 | Point Cloud RoI Pooling

    目录 目标检测 | Point Cloud RoI Pooling Point Cloud RoI Pooling 概述 Pooling RoI Pooling Point Cloud RoI Poo ...

  6. 5分钟体验代码仓托管、CloudIDE云端代码编辑、调试、运行

    摘要:您将学会如何通过代码托管(CodeHub)创建代码仓,解决软件开发者在跨地域协同.多分支并发.代码版本管理.安全性等方面的问题. 本文分享自华为云社区<5分钟体验代码仓托管.CloudID ...

  7. 华为云NFT云宝限量开抢,区块链技术为你的数字资产保驾护航

    摘要:正是有这些底层的区块链技术支撑,才能让我们真正的"拥有"独属于自己的NFT数字资产. 本文分享自华为云社区<华为云独家NFT限量开抢,背后的这些技术你都知道吗?> ...

  8. 基于MRS-Hudi构建数据湖的典型应用场景介绍

    摘要:华为云FunsionInsight MRS已集成Apache Hudi 0.8版本,基于MRS-Hudi构建数据湖解决方案. 本文分享自华为云社区<基于MRS-Hudi构建数据湖的典型应用 ...

  9. 一文带你从零认识什么是XLA

    摘要:简要介绍XLA的工作原理以及它在 Pytorch下的使用. 本文分享自华为云社区<XLA优化原理简介>,作者: 拓荒者01. 初识XLA XLA的全称是Accelerated Lin ...

  10. docker镜像列表存在但删除显示 No such image问题解决

    近期使用了docker,但删除镜像时候遇到了无法删除问题.提示:No such Image.原因有两个,解决方法如下: 原因1: 容器还存在是无法删除镜像的 解决步骤: 1.停掉容器(docker s ...