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

  项目中存在第三方系统之间的服务调用通信,且会进行频繁调用,由于很早之前实现的调用方式为每调用一次外部接口,就需要新建一个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. vue3.3新特性defineOptions

    当我们使用选项式api时候,可以轻松创建与setup()选项同级别的选项. 但是,用了

  2. 【eBPF-01】初见:基于 BCC 框架的第一个 eBPF 程序

    闲言少叙,本文记录了如何零基础通过 BCC 框架,入门 eBPF 程序的开发,并实现几个简易的程序. 有关 eBPF 的介绍,网络上的资料有很多,本文暂且先不深入讨论,后面会再出一篇文章详细分析其原理 ...

  3. 为什么说数字孪生和GIS高度互补?它们各自从对方那里获得了什么?

    在数字化时代,数字孪生和GIS作为两项重要技术,它们的融合正日益受到人们的关注和认可.数字孪生是将实体世界与数字世界紧密结合的技术,可以创建实时的虚拟副本,对物理系统进行模拟.优化和预测.而GIS则是 ...

  4. selenium之下拉菜单列表定位

    下拉菜单列表定位>>使用Select类定位 from selenium.webdriver.support.ui import Select #导入Select类 select=Selec ...

  5. MySQL|主从延迟问题排查(二)

    二.案例分享二 2.1 问题描述 主库执行insert  select 批量写入操作,主从复制通过row模式下转换为批量的insert大事务操作,导致只读实例CPU资源以及延迟上涨 16:55-17: ...

  6. OBS鉴权实现的宝典秘籍,速拿!

    摘要:OBS提供了REST(Representational State Transfer)风格API,支持您通过HTTP/HTTPS请求调用.本文将带你了解OBS API鉴权实现的宝典秘籍. OBS ...

  7. 云图说|图解制品仓库CodeArts Artifact

    摘要:制品仓库用于存放由源码编译生成的.可运行的二进制文件,重要作用是实现制品文件的可信存储,支撑软件开发活动. 本文分享自华为云社区<[云图说]第277期 图解制品仓库CodeArts Art ...

  8. 云小课丨SA基线检查:给云服务来一次全面“体检”

    摘要:随着企业上云进程的加快,由于云服务配置不合理.不合规等引发的安全风险与日俱增.如果没有加以重视并做及时的诊断处置,将会对企业云上业务带来巨大的安全隐患. 本文分享自华为云社区<云小课丨安全 ...

  9. 上手测试GaussDB(for Redis) 和开源 Redis,只为推荐质优价廉的Redis

    摘要:一文带你全方位测评 GaussDB(for Redis) 和开源 Redis. 本文分享自华为云社区<程序员硬核测评:全方位测评 GaussDB(for Redis) 和开源 Redis& ...

  10. “pip不是内部或外部命令,也不是可运行的程序或批处理文件” 到底有多么神秘

    摘要:pip不是内部或外部命令,也不是可运行的程序或批处理文件到底有多么神秘? 本文分享自华为云社区<揭开「pip不是内部或外部命令,也不是可运行的程序或批处理文件」的神秘面纱>,作者:A ...