495 lines
31 KiB
Markdown
495 lines
31 KiB
Markdown
# 头程国内、尾程海外的跨区域微服务部署方案
|
||
|
||
更新时间:2026-07-29
|
||
依据:当前 `D:/baiyue` Spring Cloud Alibaba 微服务、`悦/头程物流流程图.svg`、头程下单原型和货代后台原型。
|
||
|
||
> 审核状态:待用户审核。本文是项目拆分、部署和迁移的实施方案;文中目标架构、海外资源和切流步骤不代表已经完成生产部署。当前生成的部署目录仅是同一 Git 仓库内的 Maven 聚合和配置骨架。
|
||
|
||
## 1. 结论
|
||
|
||
推荐采用“国内业务单元 + 海外业务单元 + 跨区域可靠事件桥”,不要把同一套 Nacos、MySQL、Redis 或一个微服务调用链直接横跨国内外服务器。
|
||
|
||
这里的“国内业务单元、海外业务单元”首先是代码、数据库和数据所有权边界,不等于一期必须立即购买海外服务器。当前项目应立即按两个部署单元拆分配置、服务职责和事件契约,但一期允许所有服务都部署在国内同一机房:`lastleg-service`、海外仓实例和海外集成实例先以 `REGION_CODE=CN` 运行。待事件链路、鉴权和对象存储满足迁移门槛后,再把尾程写入单元迁到海外,不需要重新拆项目。
|
||
|
||
```text
|
||
国内单元:客户、订单、头程、国内仓、认证和权限的主数据
|
||
海外单元:尾程、海外仓、清关、提柜、派送、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`。
|
||
|
||
```powershell
|
||
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. 推荐部署拓扑
|
||
|
||
```mermaid
|
||
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`,这不是尾程交接事件。尾程只能在真实“装船发运”完成后建立预到港任务。
|
||
|
||
建议新增:
|
||
|
||
```text
|
||
FirstLegShipmentDeparted.v1
|
||
LastMileHandoffAccepted.v1
|
||
LastMileHandoffRejected.v1
|
||
LastMileMilestoneChanged.v1
|
||
LastMileCompleted.v1
|
||
```
|
||
|
||
### 5.2 交接事件示例
|
||
|
||
```json
|
||
{
|
||
"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 和消息签名。
|
||
|
||
如果暂时没有全局流量产品,可以先使用两个域名:
|
||
|
||
```text
|
||
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 集群。配置必须增加:
|
||
|
||
```text
|
||
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`。
|
||
|
||
```text
|
||
头程本地状态 -> 国内轨迹投影
|
||
海外 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 + 交接回执并验证五个尾程阶段,再将同一部署单元迁到海外影子环境。
|
||
|
||
完成这三项后,国内和海外才能真正成为两个可以独立运行、网络中断后仍可恢复的业务单元。
|