Files
baiyue-firstleg/docs/cross-region-firstleg-lastleg-deployment-plan.md
T
2026-07-30 18:01:36 +08:00

31 KiB
Raw Blame History

头程国内、尾程海外的跨区域微服务部署方案

更新时间:2026-07-29
依据:当前 D:/baiyue Spring Cloud Alibaba 微服务、悦/头程物流流程图.svg、头程下单原型和货代后台原型。

审核状态:待用户审核。本文是项目拆分、部署和迁移的实施方案;文中目标架构、海外资源和切流步骤不代表已经完成生产部署。当前生成的部署目录仅是同一 Git 仓库内的 Maven 聚合和配置骨架。

1. 结论

推荐采用“国内业务单元 + 海外业务单元 + 跨区域可靠事件桥”,不要把同一套 Nacos、MySQL、Redis 或一个微服务调用链直接横跨国内外服务器。

这里的“国内业务单元、海外业务单元”首先是代码、数据库和数据所有权边界,不等于一期必须立即购买海外服务器。当前项目应立即按两个部署单元拆分配置、服务职责和事件契约,但一期允许所有服务都部署在国内同一机房:lastleg-service、海外仓实例和海外集成实例先以 REGION_CODE=CN 运行。待事件链路、鉴权和对象存储满足迁移门槛后,再把尾程写入单元迁到海外,不需要重新拆项目。

国内单元:客户、订单、头程、国内仓、认证和权限的主数据
海外单元:尾程、海外仓、清关、提柜、派送、POD、Amazon SP-API
跨区交接:本地事务 Outbox -> HTTPS/mTLS 事件中继 -> Inbox 去重 -> 处理回执
统一轨迹:海外发布里程碑事件,国内保存只读投影供客户查询

这一方案符合原型的业务边界:

  • 头程在国内完成“下单 → 到仓 → 出仓 → 装船发运”。
  • 尾程在海外完成“到港前 → 到港 → 到仓 → 出仓 → 签收”。
  • “装船发运”是两个区域的数据所有权交接点。

1.1 当前已生成的部署骨架

路径 当前用途 当前状态
D:/baiyue/deployments/cn 国内部署单元 Maven 聚合、环境变量示例和构建脚本 已生成,待审核;一期实际部署入口
D:/baiyue/deployments/overseas 海外部署单元 Maven 聚合、环境变量示例和构建脚本 已生成,待审核;暂不代表海外服务器已部署
D:/baiyue/common-event-contract 国内外共享的事件信封、类型和区域契约 已生成基础源码,6 个单元测试通过;尚未接入业务事件链路,不得视为可用于生产
D:/baiyue/event-bridge-service 跨区事件接收幂等、待发送事件和投递尝试的独立服务边界 已生成基础骨架,8 项测试通过;内部令牌、目标区域校验和 Inbox 幂等接收已实现,生产传输开关为关闭

两个部署目录引用根仓库现有模块,不复制业务源码,也不建立第二个 Git 仓库。构建命令为:

国内和海外聚合均已包含 common-event-contract 与 event-bridge-service。国内默认构建为 18 个 Reactor 模块;一期尾程临时在国内运行时,使用 phase1-domestic-lastleg Profile 构建 19 个 Reactor 模块并包含 lastleg。

cd D:\baiyue\deployments\cn
.\build.ps1

# 一期尾程临时部署在国内
.\build.ps1 -IncludePhase1Lastleg

cd D:\baiyue\deployments\overseas
.\build.ps1

只校验默认聚合关系时,在对应目录执行 mvn -f pom.xml -DskipTests validate;校验一期国内尾程兼容聚合时执行 mvn -f pom.xml -Pphase1-domestic-lastleg -DskipTests validate。事件契约可在根目录执行 mvn -pl common-event-contract -am test。以上构建通过只说明代码依赖和测试通过,不等于生产部署、网络连通或数据迁移验收通过。

2. 当前代码现状判断

模块 当前情况 对跨区部署的影响
gateway 已按服务名走 Nacos 和 lb:// 路由 可以分别部署国内、海外 Gateway,但必须连接各自区域的 Nacos
auth-service 国内统一登录,HS256 JWT 可先放国内;正式跨区前建议改为 RS256/ES256 + JWKS
iam-service 租户、角色、菜单、按钮权限 主库建议放国内,海外通过 Token 权限快照工作,不应每次跨区查询 IAM
order-service 已有下单和状态机,也有 ord_outbox_event Outbox 只是表和写入逻辑,尚缺可靠发布器、MQ/中继和跨区消费者
firstleg-service 目前只有健康检查、任务分配等少量能力 原型中的到仓、测量、排柜、订舱、装柜、报关等还需实现
lastleg-service 当前基本只有健康检查 还没有到港前、清关、提柜、海外仓、派送、POD 的业务闭环
warehouse-service 当前是一个服务边界 需要区分国内仓实例和海外仓实例,各自独占区域数据库
file-service 当前默认使用本地磁盘 跨区域部署不能继续依赖单机 D:/...,需要迁移到 OSS/S3 类对象存储
integration-service 已预留百度 OCR、Amazon SP-API 配置 国内实例启用百度 OCR;海外实例启用 Amazon SP-API 和当地承运商接口
event-bridge-service 已有端口 9300、独立库和三张基础表;内部服务令牌、目标区域、事件信封与摘要校验及 Inbox 幂等已实现,8 项测试通过 mTLS、来源白名单、RocketMQ、真实跨公网 HTTP 投递、Outbox 扫描调度和业务消费者尚未实现,不能用于生产交接

因此现在可以先搭好部署骨架和事件契约,但不能把 lastleg-service 部署到海外后就认定尾程业务已经完成。

3. 推荐部署拓扑

flowchart LR
    FE[客户/平台前端] --> EDGE[统一域名 + CDN/WAF/全局路由]

    subgraph CN[国内业务单元]
        CNGW[gateway-cn]
        AUTH[auth-service]
        IAM[iam-service]
        ORDER[order-service]
        FIRST[firstleg-service]
        CNWMS[warehouse-service CN]
        CNFILE[file-service CN]
        CNOCR[integration-service Baidu OCR]
        CNDB[(国内独立 MySQL)]
        CNREDIS[(国内 Redis)]
        CNNACOS[Nacos prod-cn]
        CNOUT[Outbox Publisher]
        CNPROJ[统一轨迹投影]
    end

    subgraph OS[海外业务单元]
        OSGW[gateway-overseas]
        LAST[lastleg-service]
        OSWMS[warehouse-service OVERSEAS]
        OSFILE[file-service OVERSEAS]
        OSINT[integration-service Amazon/海关/承运商]
        OSDB[(海外独立 MySQL)]
        OSREDIS[(海外 Redis)]
        OSNACOS[Nacos prod-overseas]
        OSIN[Inbox Consumer]
        OSOUT[Outbox Publisher]
    end

    EDGE -->|Auth/IAM/Order/First-leg| CNGW
    EDGE -->|Last-leg/海外仓作业| OSGW
    CNGW --> AUTH & IAM & ORDER & FIRST & CNWMS & CNFILE
    OSGW --> LAST & OSWMS & OSFILE & OSINT
    FIRST --> CNOUT
    CNOUT -->|HTTPS 443 + mTLS| OSIN
    OSIN --> LAST
    LAST --> OSOUT
    OSOUT -->|里程碑/回执 HTTPS 443 + mTLS| CNPROJ

3.1 国内部署的服务

服务 是否国内主部署 说明
gateway 是 国内客户、平台后台入口
auth-service 是 账号登录、刷新、退出的唯一写入口
iam-service 是 企业、成员、角色、菜单和按钮权限主数据
order-service 是 商户下单、审核、受理以及订单主档
firstleg-service 是 到国内仓、测量差异、排柜、订舱、拖车、装柜和国内报关
warehouse-service 是 国内仓资料和国内仓作业数据
file-service 是 企业资质、国内报关资料、装柜照片等国内文件入口
integration-service 是 百度 OCR、国内报关代理或港务接口适配
audit-service 是 国内业务审计主记录
print-service 按需 国内标签、箱唛、作业单打印

3.2 海外部署的服务

服务 是否海外部署 说明
gateway 是 海外运营后台、海外 PDA 和承运商回调入口
lastleg-service 是 到港前、清关、提柜、到仓、派送、签收和异常主服务
warehouse-service 是 海外仓预约、开柜、清点、打托、拣货和装车
file-service 是 BOL、DO/PU、开柜照片、POD 等海外文件
integration-service 是 Amazon SP-API、FBA 预约、海关、码头和海外承运商适配
audit-service 是 海外操作本地审计;摘要可异步回传国内
print-service 按需 BOL、派送单、海外标签和仓内作业单

海外不部署 order-service 和 firstleg-service 的写实例,国内也不部署 lastleg-service 的写实例,避免同一业务聚合在两个区域同时写入。

3.3 一期与目标物理归属

服务 一期物理位置 后期主写位置 迁移说明
auth-service、iam-service 国内 国内 统一登录和权限主数据保持国内唯一写入
order-service、firstleg-service 国内 国内 订单和头程事实保持国内唯一写入
lastleg-service 国内 海外 一期先在国内运行;后期只把新创建的尾程聚合切到海外,旧单在国内排空
warehouse-service 国内单实例 国内仓实例在国内,海外仓实例在海外 同一代码按区域配置部署,数据库完全隔离
file-service 国内 国内、海外各一套 文件元数据和对象桶按数据产生区域归属,按业务需要复制
integration-service 国内 国内、海外各一套 国内启用 OCR/港务适配,海外启用 Amazon/海关/承运商适配
audit-service、print-service 国内 按区域各自部署 本地操作本地留痕和打印,不跨区同步调用
gateway、Nacos、Redis 国内 国内、海外各一套 两区互不注册、互不直接访问 Redis

4. 原型节点与服务归属

4.1 国内头程

原型阶段 原型动作 主要服务
下单 客户下单、委托下单、快速下单、换标下单 order-service;一期仅实现普通头程订单,不扩展快速/换标完整产品
到仓 预约取货、送货上门 firstleg-service + 国内 warehouse-service
到仓 入库测量、差异核对、指定库位 国内 warehouse-service,结果事件回写 firstleg-service
出仓 智能排柜、订舱、预约拖车 firstleg-service
出仓 PDA 拣货、装柜、柜照和铅封照 国内 warehouse-service + file-service
出仓 报关资料、代理报关、回执 firstleg-service + 国内 integration-service + file-service
出仓 港务信息、预计开船、到港 firstleg-service + 船司/港务适配器
交接 装船发运 firstleg-service 生成不可变交接快照并写 Outbox

4.2 海外尾程

原型阶段 原型动作 主要服务
到港前 FBA 仓预约、BOL 预清货、清关资料、货物中转备注 lastleg-service + 海外 integration-service
到港 清关放行、DO/PU、通知码头车队提柜 lastleg-service
到仓 开柜拍照比对、PDA 打托、派送车队预约 lastleg-service + 海外 warehouse-service + file-service
出仓 BOL 送货、PDA 拣货装车、整柜派送、自提出仓 lastleg-service + 海外 warehouse-service
签收 私人仓/FBA 签收回执、送仓异常 lastleg-service + file-service + Amazon/承运商适配器

5. 跨区交接设计

5.1 正确交接点

当前 order-service 在订单 APPROVED 时写 FIRST_LEG_ORDER_APPROVED,这不是尾程交接事件。尾程只能在真实“装船发运”完成后建立预到港任务。

建议新增:

FirstLegShipmentDeparted.v1
LastMileHandoffAccepted.v1
LastMileHandoffRejected.v1
LastMileMilestoneChanged.v1
LastMileCompleted.v1

5.2 交接事件示例

{
  "eventId": "01J...ULID",
  "eventType": "FirstLegShipmentDeparted",
  "eventVersion": 1,
  "occurredAt": "2026-07-29T16:30:00+08:00",
  "traceId": "trace-xxx",
  "sourceRegion": "CN",
  "operatorTenantId": 10001,
  "customerTenantId": 20001,
  "aggregateId": 90001,
  "aggregateVersion": 7,
  "handoverVersion": 1,
  "payload": {
    "firstLegOrderIds": [80001, 80002],
    "containerNo": "MSCU1234567",
    "sealNo": "SEAL123456",
    "vesselName": "EXAMPLE VESSEL",
    "voyageNo": "V001",
    "mblNo": "MBL001",
    "hblNos": ["HBL001"],
    "etd": "2026-07-30T10:00:00+08:00",
    "eta": "2026-08-18T09:00:00-07:00",
    "destinationPortCode": "USLAX",
    "destinationWarehouse": {
      "type": "FBA",
      "code": "LGB8"
    },
    "cargoSnapshot": [],
    "documentRefs": [],
    "checksum": "sha256:..."
  }
}

文件正文不要放进消息,只传文件 ID、版本、SHA-256 和一次性下载凭证。海外确认保存后再返回 LastMileHandoffAccepted。

5.3 可靠性规则

  1. 头程状态更新、状态历史和 Outbox 必须在一个国内 MySQL 本地事务中完成。
  2. 国内发布器轮询 PENDING/RETRY Outbox,使用 HTTPS 443 + mTLS 投递到海外事件入口。
  3. 海外先按 eventId 写 Inbox,唯一索引去重,再创建尾程交接记录。
  4. 消费失败时不确认;重复投递必须返回相同业务结果。
  5. 海外校验通过后写本地 Outbox,异步回传 Accepted/Rejected 回执。
  6. 国内收到 Accepted 后才把交接状态标记为 ACCEPTED,但不能回滚已经真实发生的装船事实。
  7. 修改交接数据要发布更高 handoverVersion 的新事件,不能覆盖旧快照。
  8. 跨区断网时,两个区域本地业务继续运行;Outbox 积压告警,网络恢复后自动补投。

不建议让 firstleg-service 通过 OpenFeign 跨公网同步调用 lastleg-service 创建尾程单。国际链路一次超时就会把真实装船操作拖入不确定状态。

5.4 Topic、事件桥和订单通知

一期不强制引入跨区 MQ,可由 Outbox 发布器直接通过 HTTPS/mTLS 投递;引入 RocketMQ 后,国内和海外各使用独立集群,禁止一个 Broker 集群横跨两区。建议 Topic 如下:

Topic 方向 主要事件
shipment-handover-cn 国内 → 海外 FirstLegShipmentDeparted.v1
shipment-handover-result-os 海外 → 国内 LastMileHandoffAccepted.v1、LastMileHandoffRejected.v1
shipment-milestone-os 海外 → 国内 LastMileMilestoneChanged.v1、LastMileCompleted.v1
security-session-cn 国内 → 海外 会话撤销、账号禁用、权限版本变化

同一尾程聚合以 aggregateId 作为顺序键;只能保证同一聚合有序,不追求全局有序。事件入 Inbox 并提交本地事务后才返回成功,失败采用有上限的指数退避,超过阈值进入死信并告警。

短信、邮件、站内信和企业微信等客户通知不得放在订单事务内同步发送。国内 notification-service 或通知消费者订阅国内订单事件及海外回传的里程碑事件,按 eventId + channel + recipient 幂等发送;承运商回调在海外本地处理,再通过里程碑事件回传国内。

6. Gateway 与流量路由

建议对外保持一个域名,例如 api.example.com,由 CDN/WAF/全局流量入口按路径转发:

路径 目标区域
/api/auth/** 国内 Gateway
/api/platform/v1/iam/**、/api/merchant/v1/iam/** 国内 Gateway
/api/merchant/v1/orders/**、/api/platform/v1/orders/** 国内 Gateway
/api/merchant/v1/first-leg/**、/api/platform/v1/first-leg/** 国内 Gateway
/api/merchant/v1/last-leg/**、/api/platform/v1/last-leg/** 海外 Gateway
海外仓/PDA 专用路径 海外 Gateway

国内 Gateway 只通过国内 Nacos 发现本地服务;海外 Gateway 只通过海外 Nacos 发现本地服务。跨区内部事件入口不暴露在普通业务 Gateway 上,应使用独立内部域名、WAF 白名单、mTLS 和消息签名。

如果暂时没有全局流量产品,可以先使用两个域名:

https://api-cn.example.com
https://api-us.example.com

前端通过同一个 API SDK 根据业务域选择基础地址,不要让浏览器知道 Nacos 服务名。

一期即固定对外域名和证书,域名先指向国内 WAF/负载均衡;计划切换前至少 7 天把 DNS TTL 调整到 300 秒。后期统一域名由全局流量入口按路径转发,auth/iam/order/first-leg 固定国内,last-leg 才允许按灰度规则转发海外。DNS 只负责稳定入口,不用 DNS 权重承担租户或订单级灰度,因为 DNS 缓存无法保证同一业务聚合始终进入同一区域。

灰度期可先使用 api-us-canary.example.com 供指定运营账号和租户验证,正式流量由 Gateway 根据可信的 execution_region 路由。Gateway 必须删除客户端伪造的区域头。若前端需要访问两个域名,CORS 只允许明确的生产前端域名、必要方法和请求头;启用 Cookie 时禁止 Access-Control-Allow-Origin: *,并统一处理预检请求。优先使用同一对外域名,减少浏览器跨域和登录态问题。

7. Nacos 设计

不要使用一个 Nacos 集群横跨国内和海外,也不要让海外服务连接国内 8848。

区域 Nacos Namespace Group 注册服务示例
国内 nacos-cn.internal prod-cn BAIYUE gateway、auth、iam、order、firstleg、国内 warehouse/file/integration
海外 nacos-overseas.internal prod-overseas BAIYUE gateway、lastleg、海外 warehouse/file/integration

两个区域可以保留相同服务名,例如都叫 warehouse-service,因为它们位于不同 Nacos 集群。配置必须增加:

REGION_CODE=CN / OVERSEAS
DEPLOYMENT_CELL=prod-cn / prod-overseas
DATA_RESIDENCY=CN / OVERSEAS

当前各服务的 spring.config.import 固定读取 group=BAIYUE,所以两个区域先保持相同 Group,通过独立集群和 Namespace 隔离。若后续希望使用 BAIYUE-CN/BAIYUE-US,必须先把所有服务配置导入 URI 中的 Group 改成环境变量。

生产密钥不得放进 Nacos 明文配置,应进入云 KMS、Secret Manager 或主机受限环境变量。

8. 数据库、Redis 和文件边界

8.1 数据库

  • 国内 MySQL:baiyue_auth、baiyue_iam、baiyue_order、baiyue_firstleg、baiyue_warehouse_cn、baiyue_file_cn、baiyue_integration_cn、baiyue_audit_cn、baiyue_print_cn。
  • 一期尾程仍在国内使用 baiyue_lastleg_cn,库名提前带区域后缀,避免迁移时混淆数据所有权。
  • 海外 MySQL:baiyue_lastleg_os、baiyue_warehouse_os、baiyue_file_os、baiyue_integration_os、baiyue_audit_os、baiyue_print_os。
  • 禁止跨区数据库外键、跨区事务和双写。
  • 国内只保存海外状态的只读轨迹投影;海外仍是尾程事实的唯一写入者。
  • ID 继续使用应用生成的雪花 ID/ULID,避免区域数据库自增 ID 冲突。

数据库迁移优先采用“旧单排空、新单切换”,不移动执行中的尾程聚合。确需迁移历史数据时,先全量复制,再用 Binlog/CDC 追平;切换前短暂停止对应聚合写入,确认延迟为零、行数和关键字段校验一致后,才允许海外库转为主写。国内旧库在回滚观察期保持只读,不得与海外库同时写入。

8.2 Redis 与登录会话

当前各 Servlet 服务会访问 Redis 检查 Access Token 的 jti 会话键。若海外 lastleg-service 直接读取国内 Redis,跨区断网会导致海外所有请求鉴权失败。

推荐目标方案:

  1. Auth 使用 RS256/ES256 签发短期 Access Token,海外 Gateway/服务通过 JWKS 本地缓存验签。
  2. Access Token 有效期缩短到 10~15 分钟。
  3. 登录、撤销、禁用账号发布 SessionIssued/SessionRevoked/AccountDisabled 事件,复制到海外本地 Redis。
  4. 海外服务只访问海外 Redis,不访问国内 Redis。

以上改造是海外影子环境上线前置条件,不应放到海外正式切流之后。当前代码仍使用 HS256,并要求通过 Redis 中存在 jti 才允许访问;迁移时先支持带 kid 的 RS256/ES256 Token 和 JWKS 本地缓存,再将海外校验改为“本地验签 + 短有效期 + 本地撤销集合”。私钥只保存在国内 Auth,海外只持有公钥。过渡期国内可短暂兼容旧 Token,但海外灰度账号必须重新登录取得新 Token,稳定后移除 HS256。

PoC 阶段可以通过专线/VPN临时读取国内 Redis,但不能作为正式生产方案;Redis 的 6379 绝不能暴露公网。

8.3 文件

当前本地磁盘文件存储不适合双区域。建议:

  • 国内文件进入国内 OSS/COS/S3 兼容桶。
  • 海外文件进入海外区域对象存储桶。
  • 只复制尾程履约必需的报关资料、BOL、箱单和授权照片。
  • 文件下载使用短期预签名 URL,跨区域复制前做病毒扫描、MIME/魔数和 SHA-256 校验。
  • POD 等海外原始文件保留在海外,国内保存合规副本或文件引用。

跨境传输企业、联系人、收货地址和报关资料前,应完成数据分类、最小化、访问审计和适用地区的合规评估。

9. 统一客户轨迹

客户不应为了看完整物流轨迹分别查询国内、海外两套数据库。建议国内新增“统一轨迹只读投影”,可以先放在 order-service,后续独立为 tracking-query-service。

头程本地状态 -> 国内轨迹投影
海外 LastMileMilestoneChanged -> 跨区事件 -> 国内轨迹投影
客户 GET /api/merchant/v1/orders/{id}/timeline -> 只查国内投影

轨迹投影允许最终一致,目标延迟 P95 小于 30 秒。页面应展示“最后同步时间”,跨区中断时显示“海外轨迹同步延迟”,不能把旧状态误报成业务失败。

10. integration-service 的区域部署

现有 integration-service 不需要再增加 common-sdk。同一模块按区域配置不同适配器:

国内实例 海外实例
BAIDU_OCR_ENABLED=true BAIDU_OCR_ENABLED=false
AMAZON_SP_API_ENABLED=false AMAZON_SP_API_ENABLED=true
国内港务/报关代理适配器 Amazon SP-API、海外海关、码头、车队/承运商适配器

各业务服务只调用内部抽象接口,不直接依赖百度或 Amazon SDK。第三方回调必须校验签名、保存原始回调摘要,并按第三方事件 ID 幂等处理。

11. 网络和安全

  1. 国内与海外通过站点到站点 VPN、云企业网或专线连接;跨区业务事件只开放 HTTPS 443。
  2. 3306、6379、8848、MQ Broker 端口不得暴露公网。
  3. 跨区事件入口使用 mTLS、IP 白名单、时间戳、nonce 和 HMAC/数字签名,防重放。
  4. Gateway 清除外部伪造的 X-User-Id、X-Tenant-Id、X-App-Code 等内部头。
  5. 国内和海外使用不同数据库账号、Redis密码、内部服务 Token 和对象存储凭证。
  6. 所有交接、清关放行、提柜、装车、签收和人工覆盖动作写不可篡改审计日志。
  7. 跨区链路设置独立限流、超时、熔断和重试策略,禁止无限重试。

12. 高可用与灾备规格

12.1 正式生产推荐

每个区域至少:

  • 2 个应用节点,前置负载均衡/WAF。
  • 3 节点 Nacos 集群或云托管注册配置中心。
  • MySQL 主备/托管高可用,开启 Binlog 和时间点恢复。
  • Redis 高可用或托管版。
  • 区域本地 MQ/事件中继服务。
  • 区域对象存储和跨可用区备份。

目标建议:核心写接口可用性 99.9%,数据库 RPO ≤ 5 分钟、RTO ≤ 60 分钟,跨区事件 P95 ≤ 30 秒。

12.2 低成本一期过渡

可以先使用国内、海外各一台应用服务器加托管 MySQL/Redis/对象存储,但必须接受单机故障风险,并至少做到:

  • Docker Compose 或 systemd 自动拉起。
  • 每日全备、持续 Binlog、异地备份。
  • HTTPS、WAF/安全组、只开放 80/443。
  • Outbox/Inbox、失败重试和人工补投工具先完成。
  • 上线前做跨区断网 30 分钟并恢复补投的演练。

13. 分阶段实施顺序

阶段 A:国内完成逻辑拆分并上线

  1. 所有服务仍可只部署国内,但按 CN/OVERSEAS 部署单元拆分 Nacos 配置、库名、对象存储目录和服务启动清单。
  2. 为尾程聚合增加不可变的 execution_region,创建时确定归属,执行过程中不允许改变。
  3. 实现尾程五个阶段的国内版本,并将本地磁盘文件迁到国内对象存储。
  4. 国内先使用独立 baiyue_lastleg_cn,禁止尾程表与订单、头程或仓储跨库联表。

阶段 B:在国内打通可靠事件

  1. 实现 FirstLegShipmentDeparted.v1、交接回执和里程碑事件。
  2. 完成 Outbox 发布器、Inbox 幂等、聚合版本检查、重试、死信、人工补投和通知消费者。
  3. 事件桥先在国内回环运行,验证跨服务契约,不把第一次验证留到跨国网络。
  4. 完成重复投递、乱序、30 分钟断网、文件复制失败和恢复补投演练。

阶段 C:建立海外影子环境

  1. 建立独立海外 Nacos、Gateway、MySQL、Redis、对象存储和监控,不注册到国内 Nacos。
  2. 在切流前完成 RS256/ES256 + JWKS、海外本地撤销集合以及对象存储按需复制。
  3. 海外服务先只消费脱敏测试事件或生产镜像事件,禁止产生真实外部操作和业务回执。
  4. 对比国内结果与海外影子结果,连续 7 天满足数据、权限、性能和事件延迟门槛后才能灰度。

阶段 D:按新尾程聚合灰度

  1. 先选择内部测试租户,再按 1% → 10% → 30% → 100% 放量,每档至少观察一个完整业务周期。
  2. 只有新创建的尾程聚合进入海外并永久写入 execution_region=OVERSEAS;已经在国内执行的旧单继续留在国内直至完成。
  3. Gateway 按聚合归属路由,不按一次请求随机分流;国内订单和统一轨迹查询入口保持不变。
  4. 每档验证交接成功率、事件积压、鉴权失败率、文件可用率和租户隔离后再继续放量。

阶段 E:旧单排空与正式收口

  1. 国内停止创建新的尾程聚合,只保留旧单处理、查询和应急能力。
  2. 旧单全部完成并超过售后观察期后,将 baiyue_lastleg_cn 改为只读归档。
  3. 停止临时兼容路由和 HS256,海外成为尾程唯一写入单元;完成灾备和回滚演练后收口。

14. 上线验收门槛

  • 国内 Nacos 停止海外注册后,国内服务仍能正常下单和执行头程。
  • 国际链路断开时,真实装船操作成功且事件保留在 Outbox,不产生半完成尾程单。
  • 网络恢复后,同一事件重复投递 10 次只创建一条尾程交接记录。
  • 海外拒绝不完整交接时,国内能收到结构化拒绝原因并进入人工处理。
  • 海外里程碑乱序到达时,较旧版本不能覆盖较新状态。
  • 海外 MySQL/Redis短暂不可用时,不影响国内订单和头程。
  • 商户 A 不能访问商户 B 的交接、柜、货物、文件和 POD。
  • 未授权用户即使直接调用清关放行、装车、签收接口也返回 403。
  • POD、清关资料和装柜照片下载均使用短期授权并留下审计记录。
  • 跨区事件积压、死信、延迟和回执缺失均有告警和补投手册。

14.1 灰度继续和回滚阈值

  • 继续放量:跨区事件 P95 小于 30 秒、P99 小于 2 分钟,交接成功率不低于 99.9%,Inbox 重复创建业务记录为 0,必需文件 SHA-256 校验通过率 100%。
  • 暂停放量:最老 Outbox 积压超过 5 分钟、海外接口 P95 比国内基线升高 30%、新增 401/403 比基线升高 0.2 个百分点,或出现无法自动补投的死信。
  • 立即回滚:连续 5 分钟 5xx 超过 1%、发现跨租户访问、数据丢失/重复写入、错误业务状态推进或关键文件不可恢复。
  • 回滚先停止创建新的海外聚合,已有海外聚合原则上继续由海外处理,不能直接路由到国内旧库。若海外无法继续写入,必须先冻结写入,通过反向事件或 CDC 补齐国内应急库并校验后再切回;禁止两区同时写同一聚合。
  • 国内旧库、旧版本应用和路由配置至少保留 7 天回滚观察期。每次切换和回滚均记录审批人、时间点、数据校验结果和路由版本。

15. 近期 P0 清单

当前 P0 基线:部署聚合入口、公共事件契约和事件桥 Inbox 幂等基础骨架已经生成;头程真实发运、尾程聚合、可靠发布传输、统一轨迹和通知仍未闭环。

  1. P0-01:冻结装船发运交接点和 FirstLegShipmentDeparted.v1 字段,给尾程聚合增加不可变 execution_region、聚合版本和唯一幂等键。验收:重复交接只创建一个聚合,旧版本不能覆盖新版本。
  2. P0-02:完成头程到仓、测量、排柜、订舱、装柜、报关和装船发运最小流程,生成带版本与摘要的不可变交接快照。验收:前置条件不满足不能发运,关键证据可审计。
  3. P0-03:在现有 event-bridge-service 基础上补齐 Outbox 扫描和发布状态机、有限重试、死信、人工补投及指标。验收:业务提交和事件不丢失,进程重启后可继续投递。
  4. P0-04:实现 Inbox 到尾程业务消费者和接受/拒绝回执,并完成尾程到港前、到港、到仓、出仓、签收五阶段。验收:重复、乱序、坏摘要和非法状态流转均有自动化测试。
  5. P0-05:先在国内回环打通事件桥,完成事件签名、来源白名单和 mTLS;RocketMQ 发布消费与真实跨区 HTTPS 投递按最终传输选型实现。验收:断网 30 分钟后可自动恢复补投,链路无跨库和同步跨区 Feign。
  6. P0-06:将文件存储抽象为对象存储,完成版本、病毒扫描、SHA-256 和短期下载授权。验收:交接所需文件可按清单受控复制且下载有租户审计。
  7. P0-07:把 JWT 改为 RS256/ES256 + JWKS,移除海外服务对国内 Redis 正向会话键的依赖。验收:海外断开国内 Redis 后仍可校验短期令牌和本地撤销信息。
  8. P0-08:建立 CN/OVERSEAS 配置模板、区域化数据库名和 REGION_CODE/DEPLOYMENT_CELL/DATA_RESIDENCY 启动校验。验收:区域错配时服务拒绝启动,两区不共享 MySQL、Redis 和 Nacos。
  9. P0-09:实现国内统一轨迹投影和异步订单通知。验收:重复不新增、乱序不回退、通知失败不回滚业务且可死信补投。
  10. P0-10:建立覆盖断网、重复、乱序、跨租户、文件失败和回滚的自动化验收。验收:国内回环全部通过后才允许建设海外影子环境。

16. 最终建议

现在最合适的落地路线不是立即购买很多服务器,而是先完成三个基础能力:

  1. 把 firstleg-service 的真实“装船发运”节点做出来。
  2. 把现有 Outbox 表补成可发布、可重试、可告警的可靠事件链路。
  3. 在国内先为 lastleg-service 实现 Inbox + 交接回执并验证五个尾程阶段,再将同一部署单元迁到海外影子环境。

完成这三项后,国内和海外才能真正成为两个可以独立运行、网络中断后仍可恢复的业务单元。