RestTemplate使用长连接

2024-08-28 02:12
文章标签 使用 连接 resttemplate

本文主要是介绍RestTemplate使用长连接,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

      • 1. HTTP协议的长连接和短连接
      • 2. RestTemplate的长连接和短连接
        • 2.1 RestTemplate的构造方法:
        • 2.2 验证RestTemplate的默认请求工厂:
        • 2.3 验证SimpleClientHttpRequestFactory每次请求开启新连接:
      • 3. RestTemplate使用长连接

1. HTTP协议的长连接和短连接

HTTP协议的长连接和短连接,本质上是TCP协议的长连接和短连接。

长连接(持久连接)和短连接(非持久连接)的区别在于:
长连接:连接建立后,保持打开状态,可以复用同一连接进行多个请求和响应。减少了频繁的连接建立和断开开销,适用于需要频繁通信的场景,如 HTTP/1.1 和 WebSocket。
短连接:每次请求后,连接都会关闭。每次新的请求都需要重新建立连接,适用于通信频率较低的场景,如 HTTP/1.0。

在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。

从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入:Connection:keep-alive。在使用长连接的情况下,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件中设定这个时间。

2. RestTemplate的长连接和短连接

2.1 RestTemplate的构造方法:

RestTemplate的构造方法:
RestTemplate()
RestTemplate(ClientHttpRequestFactory requestFactory)
RestTemplate(List<HttpMessageConverter<?>> messageConverters)

ClientHttpRequestFactory接口的实现很多,其中最常用的有以下两种:
SimpleClientHttpRequestFactory(封装URLConnection)
HttpComponentsClientHttpRequestFactory(封装HttpClient)

RestTemplate默认使用短连接,也就是每次发送请求都会建立一个新的TCP连接。这是因为RestTemplate的默认请求工厂是SimpleClientHttpRequestFactory,它没有连接池的概念。

如果需要使用长连接,可以使用HttpComponentsClientHttpRequestFactory,它支持连接池,并且可以通过setMaxTotal()和setDefaultMaxPerRoute()方法设置最大连接数和每个路由的最大连接数。

另外,需要注意的是,长连接并不是在所有情况下都能带来性能提升。在高并发场景下,使用长连接可能会导致连接池中的连接被占满,从而导致新的请求被阻塞。因此,在选择使用长连接时,需要根据实际情况进行评估和测试,以确定最合适的连接方式。

2.2 验证RestTemplate的默认请求工厂:

RestTemplate restTemplate = new RestTemplate();
System.out.println(restTemplate.getRequestFactory());

打印结果:
org.springframework.http.client.SimpleClientHttpRequestFactory@5d740a0f

代码追踪:
在RestTemplate的默认构造方法中并没有显示指明为SimpleClientHttpRequestFactory。

public RestTemplate() {this.messageConverters = new ArrayList();this.errorHandler = new DefaultResponseErrorHandler();this.headersExtractor = new RestTemplate.HeadersExtractor();this.messageConverters.add(new ByteArrayHttpMessageConverter());this.messageConverters.add(new StringHttpMessageConverter());this.messageConverters.add(new ResourceHttpMessageConverter(false));try {this.messageConverters.add(new SourceHttpMessageConverter());} catch (Error var2) {}this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());if (romePresent) {this.messageConverters.add(new AtomFeedHttpMessageConverter());this.messageConverters.add(new RssChannelHttpMessageConverter());}if (jackson2XmlPresent) {this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());} else if (jaxb2Present) {this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());}if (jackson2Present) {this.messageConverters.add(new MappingJackson2HttpMessageConverter());} else if (gsonPresent) {this.messageConverters.add(new GsonHttpMessageConverter());} else if (jsonbPresent) {this.messageConverters.add(new JsonbHttpMessageConverter());}if (jackson2SmilePresent) {this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());}if (jackson2CborPresent) {this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());}this.uriTemplateHandler = initUriTemplateHandler();}

发现:RestTemplate extends InterceptingHttpAccessor implements RestOperations
然后查看InterceptingHttpAccessor ,发现:InterceptingHttpAccessor extends HttpAccessor
然后查看HttpAccessor,发现定义了成员变量private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
所以从代码角度验证了默认请求工厂为SimpleClientHttpRequestFactory。
不知道怎么追踪源码的时候,可以打上断点,然后一步一步深入到方法里面,然后去看变量是哪一步被赋值的。

2.3 验证SimpleClientHttpRequestFactory每次请求开启新连接:

示例代码如下:

RestTemplate restTemplate = new RestTemplate();
System.out.println(restTemplate.getRequestFactory());
ResponseEntity<String> response = restTemplate.getForEntity("https://blog.csdn.net", String.class);
HttpHeaders headers = response.getHeaders();
System.out.println("Response Headers: " + headers);

代码打印包含:Connection:“keep-alive”。

代码追踪:
restTemplate.getForEntity->this.execute->this.doExecute->this.createRequest->this.getRequestFactory().createRequest
可以看到先获取了请求工厂然后才建立连接的。追踪一下SimpleClientHttpRequestFactory的实现

    public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {HttpURLConnection connection = this.openConnection(uri.toURL(), this.proxy);this.prepareConnection(connection, httpMethod.name());return (ClientHttpRequest)(this.bufferRequestBody ? new SimpleBufferingClientHttpRequest(connection, this.outputStreaming) : new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming));}

openConnection 方法:
这个方法创建一个新的 HttpURLConnection 实例。openConnection 通常会调用 URL.openConnection() 方法,这实际上是创建一个新的连接对象。该连接是通过 HttpURLConnection 类建立的,它负责处理 TCP 连接的创建和管理。

prepareConnection 方法:
在这一步,prepareConnection 方法配置 HttpURLConnection 对象,例如设置请求方法(GET、POST 等)、请求头以及其他必要的配置。

SimpleBufferingClientHttpRequest 和 SimpleStreamingClientHttpRequest:
根据 bufferRequestBody 的值,返回一个适当的 ClientHttpRequest 实例。SimpleBufferingClientHttpRequest 和 SimpleStreamingClientHttpRequest 都是对 HttpURLConnection 的封装,负责处理请求的具体细节。

总结:
SimpleClientHttpRequestFactory 在每次调用 createRequest 时都会创建一个新的 HttpURLConnection 实例。虽然 HttpURLConnection 可以复用连接(通过 HTTP/1.1 的 Keep-Alive 特性),但 SimpleClientHttpRequestFactory 本身不管理连接池或长期连接的复用。

3. RestTemplate使用长连接

简单实现:

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;public static void main(String[] args) {CloseableHttpClient httpClient = HttpClients.custom().setMaxConnTotal(100).setMaxConnPerRoute(20).build();HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(factory);System.out.println(restTemplate.getRequestFactory());ResponseEntity<String> response = restTemplate.getForEntity("https://blog.csdn.net", String.class);HttpHeaders headers = response.getHeaders();System.out.println("Response Headers: " + headers);}

复杂实现:

HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();try {// 设置信任ssl访问SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();httpClientBuilder.setSSLContext(sslContext);HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()// 注册http和https请求.register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslConnectionSocketFactory).build();// 使用Httpclient连接池的方式配置PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);poolingHttpClientConnectionManager.setMaxTotal(200);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);CloseableHttpClient httpClient = httpClientBuilder.build();HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(factory);} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {logger.error("初始化Httpclient连接池出错", e);}return null;

建议:
如果你的应用有特定的安全要求或协议需求(比如使用特定的证书、主机名验证规则等),建议你使用自定义的 SSLConnectionSocketFactory 和 Registry,即复杂实现。如果你的应用场景对默认配置没有特殊要求,可以使用默认配置,即简单实现。

具体区别如下:
HTTPS 连接的 SSL/TLS 配置:
SSL/TLS 设置:如果你省略了 SSLConnectionSocketFactory 的设置,HTTPS 连接将使用默认的 SSL/TLS 配置。这可能会导致你无法使用自定义的信任策略或证书设置。
主机名验证:省略 HostnameVerifier 相关的设置将导致默认的主机名验证机制被应用,可能会有不同的验证标准。

连接工厂注册:
注册表配置:省略 Registry 的设置,将会失去对 HTTP 和 HTTPS 连接工厂的自定义管理。这可能会导致连接池无法正确处理不同协议的连接,从而影响连接的创建和管理。

连接池行为:
连接工厂选择:如果你不提供自定义的 Registry,连接池可能无法正确区分和使用 HTTP 和 HTTPS 连接工厂,从而可能导致连接创建或复用的问题。

默认配置影响:
安全性:默认的 SSL 配置可能不符合你特定的安全要求。如果你需要特定的证书信任策略或加密设置,省略自定义配置会导致无法满足这些要求。
兼容性:如果你与特定的服务或API有特定的兼容性需求,默认配置可能不满足这些需求。

这篇关于RestTemplate使用长连接的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1113438

相关文章

使用Python实现矢量路径的压缩、解压与可视化

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,... 目录引言核心功能概述1. 路径命令解析2. 路径数据压缩3. 路径数据解压4. 可视化代码实现详解1

Pandas透视表(Pivot Table)的具体使用

《Pandas透视表(PivotTable)的具体使用》透视表用于在数据分析和处理过程中进行数据重塑和汇总,本文就来介绍一下Pandas透视表(PivotTable)的具体使用,感兴趣的可以了解一下... 目录前言什么是透视表?使用步骤1. 引入必要的库2. 读取数据3. 创建透视表4. 查看透视表总结前言

Python 交互式可视化的利器Bokeh的使用

《Python交互式可视化的利器Bokeh的使用》Bokeh是一个专注于Web端交互式数据可视化的Python库,本文主要介绍了Python交互式可视化的利器Bokeh的使用,具有一定的参考价值,感... 目录1. Bokeh 简介1.1 为什么选择 Bokeh1.2 安装与环境配置2. Bokeh 基础2

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Pandas使用AdaBoost进行分类的实现

《Pandas使用AdaBoost进行分类的实现》Pandas和AdaBoost分类算法,可以高效地进行数据预处理和分类任务,本文主要介绍了Pandas使用AdaBoost进行分类的实现,具有一定的参... 目录什么是 AdaBoost?使用 AdaBoost 的步骤安装必要的库步骤一:数据准备步骤二:模型

使用Pandas进行均值填充的实现

《使用Pandas进行均值填充的实现》缺失数据(NaN值)是一个常见的问题,我们可以通过多种方法来处理缺失数据,其中一种常用的方法是均值填充,本文主要介绍了使用Pandas进行均值填充的实现,感兴趣的... 目录什么是均值填充?为什么选择均值填充?均值填充的步骤实际代码示例总结在数据分析和处理过程中,缺失数

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

解决Maven项目idea找不到本地仓库jar包问题以及使用mvn install:install-file

《解决Maven项目idea找不到本地仓库jar包问题以及使用mvninstall:install-file》:本文主要介绍解决Maven项目idea找不到本地仓库jar包问题以及使用mvnin... 目录Maven项目idea找不到本地仓库jar包以及使用mvn install:install-file基

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http