跨系统传输大 JSON:直接压缩,还是把数据放进 Redis?

文章目录

    • 一、先分清楚:到底是什么导致了问题?
      • [1. 消息或请求大小超过限制](#1. 消息或请求大小超过限制)
      • [2. 数据传输和解析开销增加](#2. 数据传输和解析开销增加)
      • [3. 同步等待时间过长](#3. 同步等待时间过长)
    • [二、方案一:大数据放进 Redis,通道只传任务标识](#二、方案一:大数据放进 Redis,通道只传任务标识)
      • [1. 整体交互流程](#1. 整体交互流程)
      • [2. 通知报文示例](#2. 通知报文示例)
      • [3. 这样做的好处是什么?](#3. 这样做的好处是什么?)
      • [4. 放进 Redis,并没有消除网络传输](#4. 放进 Redis,并没有消除网络传输)
      • [5. Redis 也有适用边界](#5. Redis 也有适用边界)
    • 三、要让外置数据方案可靠,需要补上哪些设计?
      • [1. 先写入数据,再发送任务通知](#1. 先写入数据,再发送任务通知)
      • [2. 使用任务标识实现幂等](#2. 使用任务标识实现幂等)
      • [3. 合理设置有效期](#3. 合理设置有效期)
      • [4. 回调与查询相互补充](#4. 回调与查询相互补充)
      • [5. 保留协议与数据校验信息](#5. 保留协议与数据校验信息)
    • [四、方案二:GZIP 压缩后,通过原通道传输](#四、方案二:GZIP 压缩后,通过原通道传输)
      • [1. GZIP 与 Base64 的作用不同](#1. GZIP 与 Base64 的作用不同)
      • [2. 压缩解决什么问题?](#2. 压缩解决什么问题?)
      • [3. 压缩带来的成本](#3. 压缩带来的成本)
      • [4. 压缩不能替代异步处理](#4. 压缩不能替代异步处理)
    • 五、两种方案应该如何选择?
    • 六、几个容易混淆的认识
    • 七、结语

两个系统对接时,最开始的交互通常很简单:系统 A 组装 JSON,通过 HTTP 或消息队列发送给系统 B,系统 B 完成处理后返回结果。

随着数据规模增加,一次请求可能包含大量明细、规则和参数,返回结果也可能很大。原本简单的接口开始出现请求耗时增长、消息超过大小限制、调用超时、重复重试等问题。

这时有两种常见处理思路:

  • 将大数据保存到 Redis 等共享存储,HTTP 或 MQ 只传任务标识和数据引用。
  • 对原始 JSON 进行 GZIP 压缩,再通过原有通道传输。

本文用"系统 A"和"系统 B"说明这两种方案。重点是交互方式和设计取舍,不依赖具体业务。

一、先分清楚:到底是什么导致了问题?

假设系统 A 需要向系统 B 提交一份包含几十万条明细的计算请求。

最直接的实现如下:

text 复制代码
系统 A
  └─ 组装大 JSON
       └─ HTTP / MQ 传输完整数据
            └─ 系统 B 解析、计算
                 └─ 返回大结果

这个过程可能遇到三类不同的问题。

1. 消息或请求大小超过限制

HTTP 网关、反向代理、应用服务器、MQ Broker 和客户端都可能存在大小限制,具体限制取决于产品与配置。

即使某个环节允许大消息,也不意味着链路上的其他环节都能接受。

2. 数据传输和解析开销增加

报文越大,通常需要越多网络带宽,同时增加序列化、反序列化和内存分配开销。

消息发送失败后,如果每次都重新发送完整数据,重试成本也会增加。

3. 同步等待时间过长

如果系统 A 发出 HTTP 请求后,一直等待系统 B 完成计算,那么等待时间不仅包括网络传输,还包括排队、解析、计算和结果生成。

即使输入数据不大,计算时间很长,也可能发生超时。

因此,需要区分两个问题:

报文太大,属于数据传输问题;处理太慢,属于任务执行与等待方式问题。

压缩主要处理前者,异步任务交互主要处理后者。两者可以结合使用。

二、方案一:大数据放进 Redis,通道只传任务标识

这个方案将"数据保存"和"任务通知"拆开。

大 JSON 不再放进通知报文,而是提前保存到双方可访问的 Redis。通知中只携带任务标识、输入位置等少量信息。

1. 整体交互流程

text 复制代码
① 系统 A 组装输入数据
          ↓
② 系统 A 将输入写入共享 Redis
   task:{taskId}:input
          ↓
③ 系统 A 通过 HTTP 或 MQ 通知系统 B
   "请处理这个 taskId,输入位于指定位置"
          ↓
④ 系统 B 受理任务并异步读取输入、执行计算
          ↓
⑤ 系统 B 将结果写入 Redis
   task:{taskId}:output
          ↓
⑥ 系统 B 通过回调或消息通知系统 A
   "任务已完成,结果位于指定位置"
          ↓
⑦ 系统 A 读取结果并更新任务状态
          ↓
⑧ 页面轮询或手动刷新,展示结果或失败原因

如果使用 HTTP 受理任务,接口应在完成必要校验和任务登记后尽快返回,不必等待实际计算结束。任务受理成功和任务计算成功是两个不同状态。

2. 通知报文示例

以下是示意结构,字段名称可按双方协议约定:

json 复制代码
{
  "taskId": "task-20261026-001",
  "inputKey": "task:{task-20261026-001}:input",
  "schemaVersion": "1",
  "compression": "none"
}

完成通知也保持较小体积:

json 复制代码
{
  "taskId": "task-20261026-001",
  "status": "SUCCEEDED",
  "outputKey": "task:{task-20261026-001}:output"
}

HTTP 或 MQ 承担的是任务通知职责,大数据通过共享存储交换。

3. 这样做的好处是什么?

首先,通知报文变小,可以避开原有通知通道承载大消息时的大小限制和高重试成本。

其次,任务受理和业务计算分开,系统 A 不需要维持一个长时间等待的 HTTP 请求,减少计算耗时引发的同步超时。

再次,只要输入仍然有效,通知失败时可以重发任务标识,不必随通知重复发送整份 JSON。任务标识也便于串联日志、状态查询、回调和结果读取。

4. 放进 Redis,并没有消除网络传输

这是一个容易被误解的地方。

原来的数据路径可能是:

text 复制代码
系统 A → 系统 B

引入 Redis 后变成:

text 复制代码
系统 A → Redis → 系统 B

数据仍然需要传输,甚至可能增加链路上的累计传输量和读写次数。

因此,这个方案的价值是将大数据移出通知通道,并改变任务等待方式,而不是保证总体网络流量一定更少。

如果瓶颈本身就是共享网络带宽,或者大 JSON 的解析成本,那么仅仅增加 Redis 不一定能改善性能。

5. Redis 也有适用边界

Redis 适合有明确生命周期、读写频繁、需要较快访问的临时数据交换场景,但不宜因为"读写快"就无限制地保存大对象。

如果输入或结果很大、需要长期保留,或者任务可能积压很久,可以考虑对象存储或文件存储:

text 复制代码
任务状态与索引 → 数据库 / Redis
大型输入与结果 → 对象存储 / 文件存储
任务通知       → HTTP / MQ

"大数据外置、通知传引用"是交互思路,Redis 只是其中一种存储选择。

三、要让外置数据方案可靠,需要补上哪些设计?

1. 先写入数据,再发送任务通知

应先完成输入数据写入,再通知系统 B。否则系统 B 收到任务时,可能找不到完整输入。

Redis 写入成功与 HTTP 或 MQ 通知成功之间,通常没有天然的跨系统原子性,需要考虑失败补偿:

  • 输入已写入,但通知失败:保留任务记录,支持重试或补发。
  • 通知已送达,但系统 A 没有收到响应:使用同一个任务标识重试,避免重复计算。

对于要求可靠投递的场景,可以结合持久化任务表、待发送记录和补偿扫描。

2. 使用任务标识实现幂等

同一任务可能因为网络抖动、消息重投或回调重试而被重复通知。

系统 B 应根据任务标识识别:任务是否已受理、是否正在执行、是否已经完成。重复通知不应简单地触发重复计算。

3. 合理设置有效期

输入数据的有效期需要覆盖排队、处理、重试和结果确认时间,不能只按"正常计算耗时"设置。

结果也应保留到消费方完成读取和业务确认。不同阶段可以采用不同保留时间,并在任务超时后做清理。

同时需要考虑 Redis 的淘汰策略和故障恢复方式。对不能丢失的任务,不应仅依赖可能过期或被淘汰的缓存记录。

4. 回调与查询相互补充

回调可能失败,也可能重复到达。

系统 B 应在结果写入成功后发送完成通知;系统 A 则应支持幂等处理回调,并通过任务状态查询或补偿扫描恢复遗漏状态。

页面只负责展示状态,不应承担后台任务可靠性的保证。

5. 保留协议与数据校验信息

建议双方约定数据结构版本、压缩方式、输入大小和必要的校验信息,并限制可访问的数据位置。

接收方应检查任务与数据引用是否匹配,避免将外部传入的任意 key 或地址直接当作可信输入读取。

四、方案二:GZIP 压缩后,通过原通道传输

如果 JSON 主要包含大量重复字段和结构化明细,GZIP 往往能够减小数据体积。但实际效果需要用真实报文测量,不能假定固定压缩比例。

整体流程如下:

text 复制代码
发送方:
JSON → UTF-8字节 → GZIP压缩 → MQ发送

接收方:
MQ接收 → GZIP解压 → UTF-8解码 → JSON解析

如果传输协议只允许文本,可以增加 Base64:

text 复制代码
发送方:
JSON → GZIP压缩 → Base64编码 → MQ发送

接收方:
MQ接收 → Base64解码 → GZIP解压 → JSON解析

1. GZIP 与 Base64 的作用不同

GZIP 用于压缩数据,Base64 用于把二进制转换成文本。

Base64 本身不会压缩数据,编码后的体积通常比压缩字节增加约三分之一,另外还可能存在报文封装开销。

例如,假设某份 JSON 原始大小为 12 MB,实测 GZIP 后为 2 MB,那么 Base64 后大约为 2.67 MB。这只是算术示例,不代表任何 JSON 都能达到这个压缩率。

如果 MQ 和客户端支持二进制消息体,可以直接发送压缩字节,通常不必再做 Base64。

2. 压缩解决什么问题?

它可以减小经过通信通道的数据量,降低带宽占用,并帮助报文满足消息大小限制。

但限制是否满足,应按目标系统实际校验的大小计算。如果接收端会解压并检查解压后体积,或者通道还存在额外封装,不能只看压缩结果。

3. 压缩带来的成本

压缩和解压需要 CPU,数据处理也会占用内存。

如果实现方式在内存中同时保存原始字符串、字节数组、压缩数据和 Base64 字符串,峰值内存可能明显高于单份报文大小。

对于较大的数据,可以评估流式压缩和解析,并对解压后的大小设置上限。

4. 压缩不能替代异步处理

即使消息已经压缩得很小,系统 B 仍然可能需要较长时间才能完成业务计算。

如果系统 A 继续同步等待完整结果,仍然可能超时。因此,压缩和异步任务交互是两个可以独立采用的设计。

五、两种方案应该如何选择?

维度 数据外置,只传引用 压缩后传完整内容
主要改变 数据与通知的传递方式 报文编码和体积
适合场景 数据较大,任务耗时长,需要重试和查询 压缩后能稳定满足通道限制,希望保留现有交互
通知或消息大小 较小 取决于压缩结果
总体传输量 不一定减少 对可压缩数据通常能够减少
额外依赖 共享存储及生命周期管理 双方压缩、解压协议
主要成本 共享存储读写、容量、状态与补偿设计 CPU、内存、兼容性处理
对计算超时的影响 配合异步受理可减少同步等待 不能单独解决长时间计算

选择前,可以先测量真实数据:原始大小、压缩后大小、序列化耗时、传输耗时、处理耗时、并发量和峰值内存。

如果压缩后体积可控,现有通道也能稳定处理,压缩方案通常改动较小。

如果单次数据很大、计算时间长、需要多次读取或独立管理输入与结果,那么将数据外置并采用异步任务交互更有价值。

两种方案也可以组合:

text 复制代码
系统 A:JSON → GZIP压缩 → 写入共享存储
                   ↓
             发送任务标识和数据引用
                   ↓
系统 B:读取压缩数据 → 解压 → 处理 → 保存结果 → 通知完成

组合使用时,仍然需要测量压缩收益是否足以覆盖新增的 CPU 和内存成本。

六、几个容易混淆的认识

"放进 Redis 就不会超时了。"

Redis 读写和数据解析仍然可能超时。异步受理减少的是对整个计算过程的同步等待,不代表每个步骤都没有超时风险。

"Redis 比 HTTP 快,所以所有大报文都应该放进 Redis。"

这里比较的是不同的交互路径,不能只用单次存储访问延迟判断。应看端到端耗时、容量、可靠性和维护成本。

"Base64 可以进一步压缩。"

Base64 是编码,会增加体积。是否需要它取决于通道是否支持二进制。

"MQ 已经异步,计算再久都没关系。"

异步消息减少调用方等待,但消费处理时间、确认机制、重投和幂等仍然需要设计。

"只要消息大小没有超限,就适合直接传输。"

大小限制只是一个条件,还要关注吞吐、并发、内存和失败重试成本。

七、结语

面对跨系统的大 JSON,先判断瓶颈来自哪里:是报文大小限制、带宽与解析成本,还是同步等待中的长时间计算。

压缩通过减少报文字节数改善传输;外置数据通过传引用改变交互方式;异步任务通过受理、执行和结果获取分离,减少长时间等待。

把这三个问题分别想清楚,再根据真实测量结果组合方案,才能让系统在数据规模增长后仍然保持可控。

相关推荐
136096757231 小时前
RAG 第一步就错了:切分粒度决定召回上限
后端
LEE1 小时前
别把 Agent 架构画成组织架构图——子代理该不该拆,用任务级 Eval 说话
前端·后端·ai编程
合尘猫1 小时前
词元涨价近一倍后,我把 AI 客服的成本拆成了四个可优化的变量
人工智能·后端·架构
AngusTester1 小时前
自建 Git 之后怎么推代码:配 SSH 或 HTTPS,完成第一次 push
后端
用户574385305111 小时前
# 第 12 步:操作日志 + RBAC 缓存失效 + 业务模块
后端
用户574385305112 小时前
# 织链塔:12 步从零造一个 Koa 后端框架 —— 第 4 步:JSON 条件编译引擎(buildWhere)
后端
coderCN2 小时前
NestJS + Prisma + PostgreSQL
后端
苍何2 小时前
开源微信流 APP,手机微信聊天记录,可以直接给 WorkBuddy 和 Obsidian 了
后端
智驭未来掌门人2 小时前
Qt 项目不建议导入 CLion 说明
后端