当前位置:首页 > 技术文章 > 正文内容

Spring Boot 请求处理超大JSON数据 3种 方式性能对比

1. 简介

在 Web 项目开发中,超大 JSON 数据请求的情况愈发常见,比如物联网设备批量上传海量监测数据、大数据分析系统接收大规模用户行为信息等场景。

超大 JSON 数据不仅数据量庞大,可能达到几十兆甚至上百兆,而且内部结构复杂,嵌套层级深、字段繁多。若处理不当,会引发诸多问题,如内存溢出,导致应用崩溃;数据解析耗时过长,降低系统响应速度,影响用户体验;还可能因数据传输不稳定,造成数据丢失或损坏。

因此,如何让 Spring Boot 应用高效、稳定地处理超大 JSON 数据请求,成为保障系统性能与可靠性的关键。

运用三种不同的方法,对处理超大 JSON 数据的性能展开对比分析。

2.实战案例

准备数据

public record User(Long id, String name, Integer age, String sex, String email, String address) {
}

准备了50W条json数据

接下来的测试都是基于此数据进行测试。

2.1 普通处理方式

@PostMapping("/normal")
public ResponseEntity<String> normal(@RequestBody List<User> datas) {
  datas.forEach(this::processItem) ;
  try {
    TimeUnit.MILLISECONDS.sleep(2000) ;
  } catch (InterruptedException e) {}
  return ResponseEntity.ok("normal success") ;
}
private void processItem(User item) {
  // todo
}

这是一个普通 POST 请求处理方法。使用 @RequestBody 接收客户端传来的 List<User> 类型 JSON 数据。这种方式在数据量大时可能因同步处理和内存占用导致性能瓶颈。

如下,通过JConsole查看,在处理整个请求过程中内存的变化情况:

只要进入到该方法中内存就已经暴增到最高点。

2.2 使用流式解析

使用 Jackson 的 JsonParser 逐块读取数据,不加载整个 JSON 到内存。

@PostMapping("/stream")
public ResponseEntity<String> stream(InputStream inputStream) throws IOException {
  ObjectMapper mapper = new ObjectMapper();
  try (JsonParser parser = mapper.getFactory().createParser(inputStream)) {
    // 遍历 JSON 结构
    JsonToken token;
    while ((token = parser.nextToken()) != null) {
      if (token == JsonToken.START_ARRAY) {
        while (parser.nextToken() != JsonToken.END_ARRAY) {
          // 逐条处理数组中的对象
          User item = parser.readValueAs(User.class) ;
          processItem(item);
        }
      }
    }
  }
  try {
    TimeUnit.MILLISECONDS.sleep(2000) ;
  } catch (InterruptedException e) {}
  return ResponseEntity.ok("stream success");
}

通过流式处理超大 JSON 数据。利用 InputStream 读取数据,借助 JsonParser 逐个解析 JSON 令牌。当遇到数组起始时,逐条读取并处理 User 对象,避免一次性加载全部数据到内存,有效降低内存占用。

如下,通过JConsole查看,在处理整个请求过程中内存的变化情况:

整体内存变化非常的小。当我们没有进入while循环处理的时候,内存基本是没有变化的,只有再真正处理数据的时候,内存才会递增。

2.3 使用响应式WebFlux

使用 Spring WebFlux 实现非阻塞流式处理。

@PostMapping(value = "/reactive")
public Mono<String> fluxJson(@RequestBody Flux<User> items) {
  return items.doOnNext(this::processItem)
      .doOnComplete(() -> {
        System.err.println("处理完成...") ;
      })
      .then(Mono.just("flux success"))
      .delayElement(Duration.ofSeconds(2)) ;
}

访问该接口,最终内存情况如下:

与上面的InputStream处理方式一样,只要没有开始处理数据,内存基本不会变化,只有开始处理后内存才开始递增。

相关文章

7种超轻量级的Linux发行版,能够帮助你找到适合自己的操作系统

Linux是一种非常受欢迎的开源操作系统,而且有许多版本可以选择。有时候,你需要一种超轻量级的Linux发行版,它可以在资源有限的设备上运行,并且能够快速启动。本文将介绍7种超轻量级的Linux发行版...

细数5款国外热门Linux发行版(linux发行版排名网站)

Linux系统已经与我们的生活息息相关,当你用Android手机浏览这篇文章时,你就已经在使用Linux系统。当然作为编程开发最热门的系统,他还有很多专注于开发使用的版本。Fedora热门入门推荐,一...

gitlab简单搭建与应用(gitlab怎么用)

一、gitlab1、简介GitLab是利用Ruby on Rails一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。与Github类似,GitLab...

Gitlab 的使用和代码审查流程介绍

1、先简洁介绍下项目常用的信息-面板统计页面2、用户信息面板3、服务器信息4、项目信息5、重点介绍代码提交审核机制和授权合并机制开发人员推送代码的时候不能直接推送到master,否则就会报错。此时开发...

我常在使用的几个 VIM 插件(我常在使用的几个 vim 插件)

今天给你分享几个我觉得还不错的 VIM 插件,也许能给你带来一点「惊喜感」。vim主题插件 你完全可以让你的编辑器按照你喜欢的样子呈现,在 vimcolors 这个网站中,汇集了很多的主题,你可以进去...

HTML5与APP的抉择(h5与app的区别)

同为当下炙手可热的技术,围绕APP和HTML5难免少不了各种争辩。而在“互联网+”时代,许多面临转型的传统企业,也在选择转型工具时,陷入了HTML5或APP的纠结抉择之中……到底该选择HTML5还是A...