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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他