环境:Spring Boot 3.5.0
1. 简介
在金融行业批量交易记录导入、医疗系统批量患者检查报告上传以及物联网数据上报等场景中,应用常需处理用户上传的批量数据、设备传感器日志等超大JSON请求(如单次100MB)。传统 @RequestBody 反序列化方式会一次性加载完整JSON到内存,导致堆内存溢出(OOM),尤其在并发请求时易引发服务崩溃,严重影响系统可用性。
解决方案并非升级更大的实例,而是转变思路:不再将整个数据加载到内存,转而采用流式处理,逐段解析和处理数据,从而有效降低内存占用。
2.实战案例
2.1 危险处理方式
大多数JSON库都倾向于先完整读取所有内容。它们会解析全部文本、构建对象。对于小型请求,这种做法基本上没有任何问题;但面对数百MB的数据时,它会悄然耗尽内存。如下示例:
private final ObjectMapper objectMapper ;public LargeController(ObjectMapper objectMapper) { this.objectMapper = objectMapper;}@PostMappingpublic ResponseEntity>() {}); this.process(students) ; return ResponseEntity.ok("success") ;}public void process(List
这种方式处理大 JSON 数据存在严重性能问题:
性能测试
2.2 使用流式处理
该流式处理利用 Jackson 的 JsonParser 逐个解析 JSON 数组中的对象,避免将整个请求体加载到内存。每解析一个 Student 就立即处理,内存仅保留单个对象,极大降低堆占用,防止 OOM,提升处理大 JSON 文件的性能与稳定性,适用于超大请求的高效解析场景。如下示例:
private final ObjectMapper objectMapper ;@PostMapping("/2")public ResponseEntity
性能测试
结果不是很明显,由于我这里使用的请求body大小为1000条数据。
2.3 流式响应输出
如果响应内容也是非常大的JSON内容,那么也很可能导致OOM错误。所以针对此种情况我们也可以进行流式的输出,如下示例:
private final StudentRepository studentRepository ;@GetMapping("/4")public void streamResponse(HttpServletResponse response) throws Throwable { response.setContentType("application/json;charset=UTF-8"); response.setHeader("Transfer-Encoding", "chunked") ; try (var gen = this.objectMapper.getFactory().createGenerator(response.getOutputStream())) { gen.writeStartArray(); this.studentRepository.queryStudents().forEach(student -> { try { gen.writeObject(student) ; gen.flush() ; TimeUnit.SECONDS.sleep(1) ; } catch (Exception e) { e.printStackTrace(); } }) ; gen.writeEndArray(); }}public interface StudentRepository extends JpaRepository
这里查询的时候我们采用流式查询。
运行结果
本站是社保查询公益性网站链接,数据来自各地人力资源和社会保障局,具体内容以官网为准。
定期更新查询链接数据 苏ICP备17010502号-11