9.2 KiB
公共导出与下载中心接入说明
本文描述当前已经实现的 RocketMQ + 事务 Outbox 版本。完整状态机、故障模型和容量门槛见公共导出中心 RocketMQ 架构设计。
1. 边界
file-service统一承载任务、可靠调度、通用 XLSX、存储、过期清理、下载鉴权和指标。- File 不跨库读取 IAM、Order、Warehouse 等业务数据;业务服务只实现标准只读 Provider。
- MQ 只传任务标识,不传筛选条件、业务行、Token 或文件。
- 订单与客户存储是
FILE_STORAGE_ROOT目录且按单节点部署;多实例前必须使用跨主机共享卷或对象存储。 - 当前首个类型为
CUSTOMER_LIST,由 IAM 提供数据。
2. 执行链路
Admin
-> POST /api/platform/v1/files/export-tasks
-> file_export_task_guard 行锁下复核重复提交及活动上限
-> 同一事务写 file_export_task + file_export_outbox
-> Publisher 投递 RocketMQ(失败无限退避)
-> Consumer 按 dispatch_version + 数据库租约领取(并发 4)
-> POST /internal/exports/v1/{TYPE}/batches
-> 通用 SXSSFWorkbook 流式写盘
-> 条件提交 SUCCEEDED
-> GET /api/platform/v1/files/export-tasks/{id}/download
PENDING -> RUNNING -> SUCCEEDED -> EXPIRED
-> PENDING(自动重试,dispatch_version + 1)
-> FAILED -> PENDING(人工重试,dispatch_version + 1)
消息补投保持同一版本;只有执行失败、执行租约恢复和人工重试递增版本。独立 watchdog 覆盖 Provider 拉取和最终写盘,最终成功 SQL 同时校验任务状态、版本、租约所有者与数据库 deadline。
Reconciler 负责补写缺失 Outbox、同版本重发长期 SENT 命令、恢复超时执行租约、释放超时 Outbox 租约及过期文件清理。旧 ExportTaskWorker 已删除,避免绕过版本和退避窗口。
3. 浏览器 API
POST /api/platform/v1/files/export-tasksGET /api/platform/v1/files/export-tasksGET /api/platform/v1/files/export-tasks/{id}GET /api/platform/v1/files/export-tasks/{id}/downloadPOST /api/platform/v1/files/export-tasks/{id}/retry
创建示例:
{
"exportType": "CUSTOMER_LIST",
"format": "XLSX",
"taskName": "客户列表",
"parameters": { "keyword": "柏悦", "status": "ACTIVE" }
}
创建按注册表校验访问域、启用状态和来源权限。客户导出要求 platform:customer:export;列表和下载要求 platform:download:list。默认只能访问本人任务,platform:download:all 可访问同一租户与应用域的全部任务。人工重试只允许 FAILED,并重新校验类型启用状态和来源权限。
限制:单用户最多 3 个活动任务、单类型最多 5 个活动任务;同一用户 60 秒内相同类型和规范化参数返回已有活动任务。
4. Provider 契约
POST /internal/exports/v1/{exportType}/batches
X-Internal-Service-Token: ${INTERNAL_SERVICE_TOKEN}
Content-Type: application/json
请求使用 common-core 的 ExportBatchRequest,必须包含 taskId、tenantId、appCode、requestedBy、dispatchVersion、parameters、cursor、snapshotToken 和 limit。Provider 必须按身份字段约束查询,不得信任 parameters 内的租户字段。
响应使用 ExportBatchResponse:首批返回 schemaVersion、schemaHash、snapshotToken、columns、rows、游标和估算总行数;后续批次必须保持 schemaHash,可省略 columns。File 在 Feign 解码流读取阶段限制单批 4MB,再校验游标推进、列/行/文件上限,并支持 STRING、INTEGER、DECIMAL、BOOLEAN、DATE、DATETIME 六类 Excel 单元格。
IAM CUSTOMER_LIST Provider 强制 tenantId=0、appCode=PLATFORM,采用 HMAC 签名的最大商户 ID 快照。因此不会混入开始后新增客户,但不冻结期间的修改与删除。
新增类型只需:业务服务实现标准 Provider、在 baiyue.file.export.definitions 注册受控服务/路径/权限/访问域/容量参数、补权限和测试。不得修改公共 Controller、Consumer 或 Writer。
5. MQ 与 DLQ
- Topic:
${MQ_TOPIC_PREFIX}baiyue-file-export-command-v1,4 个队列。 - Consumer:
${MQ_TOPIC_PREFIX}baiyue-file-export-worker-v1,同步并发 4。 - Producer:
${MQ_TOPIC_PREFIX}baiyue-file-export-publisher-v1。 eventId、taskId、dispatchVersion、schemaVersion同时写入 User Properties。- 业务失败从不进入 DLQ;只有无法反序列化或不支持版本的消息不确认消费。
- DLQ Handler 不解析损坏 body,只从四个属性定位,并在同一事务内把 Outbox 置
QUARANTINED、当前 PENDING 任务置FAILED。缺少定位属性只记录 CRITICAL 和 messageId,不误伤任务。 - DLQ Handler 的数据库处置失败持续重试,不允许因有限次数耗尽进入无人消费的二级 DLQ。
6. 配置与部署
关键环境变量:
ROCKETMQ_NAME_SERVER
ROCKETMQ_BROKER_IP
MQ_TOPIC_PREFIX
FILE_EXPORT_CONSUMER_CONCURRENCY
FILE_EXPORT_RECONCILER_ENABLED
FILE_EXPORT_PUBLISHER_ENABLED
FILE_EXPORT_CONSUMER_ENABLED
FILE_EXPORT_RETENTION_DAYS
FILE_STORAGE_ROOT
MQ_TOPIC_PREFIX 是 File 部署必填项但允许生产空值:测试环境显式填 test-,生产环境显式填空字符串。Compose 单服务部署会插值整份文件,因此 File 服务块本身使用空默认值,避免 Auth/IAM 等无关服务被 MQ 配置阻断;deploy-services.sh 只在部署 File 时按环境强校验该变量与 ROCKETMQ_NAME_SERVER。
当前 RocketMQ Compose 不映射 Broker 宿主机端口,File 与 Broker 必须同在 infra-network,因此 ROCKETMQ_BROKER_IP 填 rocketmq-broker。Topic 初始化会等待 BaiyueCluster 真正注册,任一幂等初始化命令失败后自动重试。
File 容器初始 JVM 为 -Xms1g -Xmx2g -XX:+ExitOnOutOfMemoryError。Provider 单批连接/读取超时分别为 3 秒/15 秒;执行租约 300 秒、watchdog 60 秒、deadline 30 分钟、消费超时 90 分钟、单文件 50MB。
开发阶段确认无保留任务后,重建并执行 file/src/main/resources/db/file.sql。RocketMQ 使用 deploy/docker-compose.rocketmq.yml;脚本会先校验并展开 ROCKETMQ_BROKER_IP,再显式创建 4 队列命令 Topic、Worker 消费组、DLQ Handler 消费组以及可读写的 Worker DLQ。上线顺序:Broker -> SQL -> Nacos 注册表 -> IAM Provider -> File 三开关全关 -> Reconciler -> Publisher -> Consumer -> Admin。
生产启用并发 4 前必须完成 4 个 20 万行任务并发压测;若 CPU、堆、磁盘、Provider 或 IAM 普通接口延迟不达设计门槛,回退并发 2。
7. 观测与验收
Prometheus 指标包括任务创建/完成/耗时、队列年龄、Outbox 数量/年龄/最大尝试次数、Reconciler 动作与 export_dlq_messages_total;日志携带任务与版本标识但不打印完整数据或令牌。
最低验收:任务与 Outbox 同事务;Broker 停机恢复后追平;重复消息只生成一个有效文件;watchdog 覆盖最终写盘;deadline 竞态不能提交成功;同版本补投不递增版本;毒消息只隔离一次;IAM 快照不混入新增客户;生成的 XLSX 可真实打开且日期、金额为可计算类型;普通用户不能访问他人任务。
7.1 测试环境自动验收入口
scripts/export-center-test-env-acceptance.ps1 通过 Gateway 创建真实 CUSTOMER_LIST 任务,轮询终态,下载文件并校验 XLSX 的 ZIP 签名。Token 只通过参数或环境变量注入,脚本不会登录、部署、停止 Broker 或改数据库。
单任务冒烟:
$env:ADMIN_ACCESS_TOKEN = '<测试环境平台用户Token>'
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000'
并发 4 容量验收必须使用 4 个不同申请用户,避免 60 秒重复提交合并,并要求每个账号都具备 platform:customer:export 与 platform:download:list:
$tokens = @($env:EXPORT_TOKEN_1, $env:EXPORT_TOKEN_2, $env:EXPORT_TOKEN_3, $env:EXPORT_TOKEN_4)
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000' `
-AccessTokens $tokens -TaskCount 4 -MinimumRows 200000 -TimeoutSeconds 3600
故障注入分两段执行:故障期间使用 -CreateOnly 创建并保存任务 ID;恢复 Broker、File 或 IAM 后,用相同申请人的 Token 和 -TaskIds 继续轮询与下载。这样停止/恢复基础设施仍由有权限的运维操作,脚本只验证任务是否最终追平:
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000' -AccessTokens $tokens -TaskCount 4 -CreateOnly
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000' -AccessTokens $tokens `
-TaskIds @('<taskId1>', '<taskId2>', '<taskId3>', '<taskId4>') -MinimumRows 200000
脚本通过只证明 API 创建、数据库状态机、MQ 触发、Provider、XLSX 写入和下载主链路。CPU/堆/磁盘、Provider P95/P99、IAM 普通接口退化比例仍须同时从 Prometheus/Grafana 取证;DLQ 毒消息必须由 mqadmin 按 §19.2 投递,不能由普通验收账号伪造。