当前位置: 首页 > article >正文

Spring RestTemplate 升级 WebClient 导致 OutOfMemoryError

Spring Boot是 Java 企业应用程序的一个非常流行的框架。与内部或外部应用程序集成的一种常见方法是通过 HTTP REST 连接。我们正在从RestTemplate升级到基于 Java NIO 的WebClient,它可以通过允许在调用 REST 服务端点时进行并发来显著提高应用程序性能。WebClients 的好处如下:

  1. 并发性: WebClient 能够同时处理多个连接而不会阻塞线程,从而实现更好的并发性。
  2. 异步 :异步编程允许应用程序在等待I/O操作完成时执行其他任务,从而提高整体效率。
  3. 性能: 非阻塞 I/O 可以用更少的线程管理更多的连接,从而减少处理并发请求所需的资源。

虽然性能有所改善,但在并发连接数相同的情况下,WebClient 会因 OutOfMemoryError 而崩溃。我们将分析 WebClient 崩溃问题以及如何排除故障和修复这些问题。

Spring RestTemplate 到 WebClient 升级

为了利用 NIO 的优势(例如并发和异步处理),我们将 REST 客户端调用从 Spring RestTemplate 升级为 WebClient,如下所示。

Spring Rest模板

  public void restClientCall(Integer id, String url,String imagePath) {

    	// Create RestTemplate instance
    	RestTemplate restTemplate = new RestTemplate();

    	// Prepare the image file
    	File imageFile = new File(imagePath);

    	// Prepare headers
   	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.MULTIPART_FORM_DATA);

    	// Prepare the request body
   	MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
    	body.add("file",new org.springframework.core.io.FileSystemResource(imageFile));

    	// Create the HTTP entity with headers and the multipart body
   	HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

    	System.out.println("Starting to post an image for Id"+id);

    	// Perform the POST request
    	ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);

    	// Print the response status code and body
    	System.out.println("Response Id "+id +":"+ responseEntity.getBody());
   	System.out.println(" Time: " + LocalTime.now());}

如下所示的 Spring WebClient

public void webHeavyClientCall(Integer id,String url, String imagePath) {

	// Create a WebClient instance
	WebClient webClient = WebClient.create();

	// Prepare the image file
        File imageFile = new File(imagePath);

	// Perform the POST request with the image as a part of the request body
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
	body.add("file", new FileSystemResource(imageFile));
        System.out.println("Image upload started "+id);
		webClient.post().uri(url).contentType(MediaType.MULTIPART_FORM_DATA).body(BodyInserters.fromMultipartData(body)).retrieve().bodyToMono(String.class).subscribe(response -> {
           System.out.println("Response Id"+id+ ":" + response);
	});
}

WebClient 导致 OutOfMemoryError

当我们在OpenJDK 11中运行这两个程序时。使用基于 NIO 的 Spring WebClient 的程序在几次迭代后导致 “java.lang.OutOfMemoryError:直接缓冲内存” ,而基于 Spring RestTemplate 的程序成功完成。以下是基于 NIO 的 Spring WebClient 程序的输出。您可以注意到报告了“java.lang.OutOfMemoryError”。

Starting to post an image for Id0

Starting to post an image for Id1

Starting to post an image for Id2

Starting to post an image for Id3

Starting to post an image for Id4

Starting to post an image for Id5

Starting to post an image for Id6

Starting to post an image for Id7

Starting to post an image for Id8

Starting to post an image for Id9

Starting to post an image for Id10

Starting to post an image for Id11

Starting to post an image for Id12

Starting to post an image for Id13

Starting to post an image for Id14

2023-12-06 17:21:46.730  WARN 13804 --- [tor-http-nio-12] io.netty.util.concurrent.DefaultPromise  : An exception was thrown by reactor.ipc.netty.FutureMono$FutureSubscription.operationComplete()

reactor.core.Exceptions$ErrorCallbackNotImplemented: io.netty.channel.socket.ChannelOutputShutdownException: Channel output shutdown

Caused by: java.lang.OutOfMemoryError: Direct buffer memory

	at java.base/java.nio.Bits.reserveMemory(Bits.java:175) ~[na:na]

	at java.base/java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:118) ~[na:na]

	at java.base/java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:318) ~[na:na]

	at java.base/sun.nio.ch.Util.getTemporaryDirectBuffer(Util.java:242) ~[na:na]

	at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:164) ~[na:na]

	at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:130) ~[na:na]

	at java.base/sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:496) ~[na:na]

	at io.netty.channel.socket.nio.NioSocketChannel.doWrite(NioSocketChannel.java:418) ~[netty-transport-4.1.23.Final.jar!/:4.1.23.Final]

	at io.netty.channel.AbstractChannel$AbstractUnsafe.flush0(AbstractChannel.java:934) ~[netty-transport-4.1.23.Final.jar!/:4.1.23.Final]

	... 18 common frames omitted

解决“OutOfMemoryError:直接缓冲内存”问题

为了解决这个问题,我们利用了 yCrash 监控工具。此工具能够在生产环境中出现中断之前预测中断。一旦它预测到环境中出现中断,它就会从您的环境中捕获 360° 故障排除工件,对其进行分析并立即生成根本原因分析报告。它捕获的工件包括垃圾收集日志、线程转储、堆替换、netstat、vmstat、iostat、top、top -H、dmesg、内核参数、磁盘使用情况……

您可以在此处注册并开始使用此工具的免费版本。

yCrash 服务器分析了 Spring Boot Rest Client,并提供了问题的明确指示和建议。以下是 yCrash 为 SpringBoot WebClient 应用程序生成的事件摘要报告。您可以注意到 yCrash 清楚地指出了错误,并提供了解决问题的必要建议。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
图 1:yCrash 的事故总结报告

垃圾收集分析报告

yCrash 的垃圾收集 (GC) 分析报告显示,完整 GC 正在连续运行(见下面的屏幕截图)。当 GC 运行时,整个应用程序都会暂停,不会处理任何事务。整个应用程序将变得无响应。我们在 SpringBoot WebClient 应用程序因 OutOfMemoryError 崩溃之前观察到了无响应行为。

yCrash 报告指出了我们的连续完整 GC 问题
图 2:yCrash 报告指出了连续完整 GC 问题

日志分析报告 OutOfMemoryError:直接缓冲内存

yCrash 的应用程序日志分析报告显示,应用程序受到“ java.lang.OutOfMemoryError:直接缓冲内存” 的影响(见下面的屏幕截图),导致应用程序崩溃。

yCrash 日志报告指出 java.lang.OutOfMemoryError: Direct buffer memory
图 3:yCrash 日志报告指出 java.lang.OutOfMemoryError:直接缓冲内存

为什么Spring WebClient会出现OutOfMemoryError?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4:存储在本机内存其他区域中的 RestTemplate 对象

存储在本机内存的直接内存区域中的 WebClient 对象
图 5:存储在本机内存的直接内存区域中的 WebClient 对象

Spring WebClient 是基于Java NIO技术开发的。在 Java NIO 中,对象存储在 JVM 本机内存的“直接缓冲内存”区域中,而 RestTemplate 对象存储在 JVM 本机内存的“其他”区域中。JVM 中有不同的内存区域。要了解它们,您可以观看此视频片段。

当我们执行上述两个程序时,我们将直接缓冲区内存大小设置为 200k(即 -XX:MaxDirectMemorySize=200k)。这个大小对于 Spring RestTemplate 来说足够了,因为对象从未存储在这个区域中,但另一方面对于 Spring WebClient 来说却不够。因此 Spring WebClient 遭受了* java.lang.OutOfMemoryError: Direct buffer memory 的困扰。 *

增加 -XX:MaxDirectMemorySize

确定此问题后,我们使用 JVM 参数 -XX:MaxDirectMemorySize=1000k 将直接内存大小增加到更高的值。进行此更改后,Spring WebClient 程序运行正常,没有任何问题。

Starting to post an image for Id0

Starting to post an image for Id1

Starting to post an image for Id2

Starting to post an image for Id3

Starting to post an image for Id4

Starting to post an image for Id5

Starting to post an image for Id6

Starting to post an image for Id7

Starting to post an image for Id8

Starting to post an image for Id9

Starting to post an image for Id10

Starting to post an image for Id11

Starting to post an image for Id12

Starting to post an image for Id13

Starting to post an image for Id14

Starting to post an image for Id15

Starting to post an image for Id16

Starting to post an image for Id17

Starting to post an image for Id18

Starting to post an image for Id19

Response Id11:Image uploaded successfully!

Response Id4:Image uploaded successfully!

Response Id1:Image uploaded successfully!

Response Id18:Image uploaded successfully!

Response Id2:Image uploaded successfully!

Response Id3:Image uploaded successfully!

Response Id6:Image uploaded successfully!

Response Id5:Image uploaded successfully!

Response Id10:Image uploaded successfully!

Response Id13:Image uploaded successfully!

Response Id15:Image uploaded successfully!

Response Id8:Image uploaded successfully!

Response Id17:Image uploaded successfully!

Response Id9:Image uploaded successfully!

Response Id7:Image uploaded successfully!

Response Id0:Image uploaded successfully!

Response Id16:Image uploaded successfully!

Response Id14:Image uploaded successfully!

Response Id19:Image uploaded successfully!

Response Id12:Image uploaded successfully!

结论

在这篇文章中,我们讨论了从 Spring RestTemplate 升级到基于 Java NIO 的 WebClient 时遇到的 OutOfMemoryError 问题。我们还分享了我们采取的诊断方法以及解决问题的方法。希望您觉得它有用。


http://www.kler.cn/a/326958.html

相关文章:

  • Datawhale AI 冬令营学习笔记-零编程基础制作井字棋小游戏
  • springboot-starter版本升级es版本问题
  • Redis篇--常见问题篇7--缓存一致性2(分布式事务框架Seata)
  • 浅谈算法交易
  • Git 的基本概念和使用
  • 用python ollama qwen2.5 开发一个AI修仙游戏
  • SQL进阶技巧:如何利用if语句简化where或join中的条件 | if条件语句的优雅使用方法
  • QT界面制作
  • 粉丝们得以一窥索菲亚罗兰奢华的90岁生日庆祝仪式! 她已完成了所有的遗愿清单 !
  • 选择租用徐州存储服务器有什么作用?
  • 大数据-149 Apache Druid 基本介绍 技术特点 应用场景
  • 2024年7月大众点评广州美食店铺基础信息
  • 2024.9.24 作业
  • Stable Diffusion 蒙版:填充、原图、潜空间噪声(潜变量噪声)、潜空间数值零(潜变量数值零)
  • 我店生活系统小程序开发功能解析
  • uniapp框架中实现文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
  • 修改 idea 的 Terminal 命令窗口使用 git-bash
  • 得物App荣获新奖项,科技创新助力高质量发展
  • 国内ChatGPT镜像网站整理汇总【OpenAI o1/GPT 4o】-2024/10月最新
  • 【数据结构与算法】算法和算法分析
  • nginx服务介绍
  • ros2 colcon build 构建后,install中的local_setup.bash 和setup.bash有什么区别
  • 企业数据可视化大屏的工具选择有哪些
  • UI设计师面试整理-设计趋势和行业理解
  • 互斥量mutex、锁、条件变量和信号量相关原语(函数)----很全
  • 从两个 Excel 表格中提取相关信息,并根据学生的 学号 和 姓名 将第一个表格中的成绩数据填充到第二个表格中(附Python代码)