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使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa