2026-09-06 22:30:03 +08:00
|
|
|
|
# msgexchange-v2 架构文档
|
|
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 1. 系统定位与范围
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
msgexchange-v2 是机场 OMMS 的上游报文处理中间件,用于替换旧版 `msgexchange-api`。
|
|
|
|
|
|
它读取 CIIMS、AODB 等系统写入共享 MySQL 信箱的 XML 报文,按顺序更新航班动态,再将结果提供给下游。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
本系统负责**收报、解析、状态更新和结果投递**,不生成上游业务报文,不替代 CIIMS/AODB,也不提供 AODB 主数据编辑能力。
|
|
|
|
|
|
|
|
|
|
|
|
- **主要入口**:轮询共享 MySQL 的 `CMINMSGS`。
|
|
|
|
|
|
- **兼容入口**:`POST /cminmsgs/send`,供现役兼容、手工工具和对拍使用;写入信箱后返回记录 ID,不是生产收报主路径。
|
|
|
|
|
|
- **输出**:Kafka 的 `msg` / `schd` 消息、共享 MySQL 的 `COUTMSGS` 出站信箱,以及查询 HTTP 接口;不直接推送前端。
|
2026-09-09 17:53:08 +08:00
|
|
|
|
- **当前范围(阶段 A)**:航班当前态落自有 PostgreSQL(`FLIGHT_SCHD` + 资源明细表 + `FLIGHT_ROUTE_POINT`,权威口径见 [flight-state.md](flight-state.md))。无 Redis 依赖;ES 历史投影属暂缓的阶段 B。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
|
|
|
|
|
本文描述架构约束,不代表所有能力已实现;实现缺口见第 9 节。模块交互、状态机和参数详见 [design.md](design.md),需求见 [user-stories.md](user-stories.md)。历史报文契约仍以 [SIS 接口规范](legacy/SIS_AODB_RMS-V0.1.md) 和 [XSD](legacy/unisysaodbsis.xsd) 为兼容依据,其他 legacy 资料仅作参考。
|
|
|
|
|
|
|
2026-09-08 18:50:34 +08:00
|
|
|
|
现场供库时目标为 Oracle 11g,否则自建 PostgreSQL;当前只有 PG 实现可运行,Oracle 不是已支持的平台。
|
2026-09-09 11:43:34 +08:00
|
|
|
|
航班表结构、快照账本和处理逻辑见 [航班状态设计](flight-state.md)。
|
2026-09-08 18:50:34 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 2. 总体架构
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
CIIMS / AODB 等上游
|
|
|
|
|
|
│ 写入 XML
|
|
|
|
|
|
▼
|
|
|
|
|
|
共享 MySQL:CMINMSGS
|
|
|
|
|
|
│ 轮询未处理记录
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌──────────────── msgexchange-v2(单实例)────────────────┐
|
|
|
|
|
|
│ ingress:发现报文 → PostgreSQL 持久化入队 │
|
|
|
|
|
|
│ │ │
|
|
|
|
|
|
│ processing:取 FIFO 队头 → 解析 / 去重 → Handler 决策 │
|
2026-09-07 16:12:00 +08:00
|
|
|
|
│ └─ PG 单事务:航班变更 + 终态 + 待发事件│
|
2026-09-07 15:10:57 +08:00
|
|
|
|
│ │
|
2026-09-09 17:53:08 +08:00
|
|
|
|
│ jobs:独立维护线程(回填补偿 / 历史归档 / 留痕清理) │
|
2026-09-07 15:10:57 +08:00
|
|
|
|
│ delivery:读取 PG 待发事件 → 投递 / 重试 │
|
|
|
|
|
|
└─────────────────────────┬──────────────────────────────┘
|
|
|
|
|
|
├─ Kafka:msg / schd
|
|
|
|
|
|
└─ 共享 MySQL:COUTMSGS
|
|
|
|
|
|
|
|
|
|
|
|
处理结果提交后,再回填 CMINMSGS 的处理标记;失败需补偿。
|
|
|
|
|
|
查询接口读取航班动态,不参与状态写入。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-09 17:53:08 +08:00
|
|
|
|
收报、处理、投递与维护作业各使用独立线程,不占用 HTTP 事件循环。**航班当前态的写入只发生在持有 `PIPELINE_LOCK` 的事务内**,由主泵串行驱动。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
采用 Kotlin + JDK 25、Micronaut 编译期依赖注入和 JDBC 持久化。数据库变更由 Flyway 管理,但只作用于自有 PostgreSQL。具体依赖版本以 `build.gradle.kts` 为准,不在架构文档重复维护。
|
2026-09-07 10:27:44 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 3. 模块职责
|
2026-09-07 10:27:44 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| 模块 | 职责与边界 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `ingress` | 轮询信箱、持久化入队、补偿重扫及兼容 HTTP 写入;不解析业务报文。 |
|
|
|
|
|
|
| `codec` | XML 解码,区分非法报文与可修复的解码失败。 |
|
2026-09-09 17:53:08 +08:00
|
|
|
|
| `processing` | FIFO 调度、业务身份绑定与去重、处理器决策(SCHD/FLOP/FDEL/ADFT)、事务提交。处理器只产出决策与落库计划,不直接触碰 Kafka。 |
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| `delivery` | 消费待发事件,负责按目标保序、`schd` 聚合、投递和失败重试。 |
|
2026-09-09 17:53:08 +08:00
|
|
|
|
| `jobs` | 回填补偿扫描、历史归档与物理清除(§8.2)、留痕保留期清理;独立线程执行,不参与 FIFO。 |
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| `domain` / `config` | 领域状态、事件和决策模型,以及运行参数。 |
|
2026-09-09 17:53:08 +08:00
|
|
|
|
| `infra` | 仓储(JDBC/stub)、外部适配器、重试、健康检查与日志;通过接口隔离基础设施。 |
|
2026-09-07 10:27:44 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 4. 主流程
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
### 收报与处理
|
|
|
|
|
|
|
|
|
|
|
|
1. `InboxPoller` 默认每秒扫描 `DATE_PROCESSED IS NULL` 的信箱记录,在自有 PG 中建立 `PROC_STATE(PENDING)`。重复扫描不能重复入队;入队失败留待重扫。
|
2026-09-09 17:53:08 +08:00
|
|
|
|
2. 主泵只处理最小未完成 `MSG_ID`。解析报文、绑定业务身份并去重后,分派给 SCHD/FLOP/FDEL/ADFT 处理器。
|
|
|
|
|
|
3. 在自有 PG 同一事务内(先取 `PIPELINE_LOCK`)保存航班状态变更(`FLIGHT_SCHD` 与明细表)、处理结果、`MSG_EVENT` 待发事件与回填待办预登记。
|
2026-09-07 16:12:00 +08:00
|
|
|
|
4. 事务提交后,回填共享信箱的处理标记(外部副作用,补偿保障)。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
|
|
|
|
|
### 投递
|
|
|
|
|
|
|
|
|
|
|
|
`Dispatcher` 从 `MSG_EVENT` 取出待发事件。普通事件按投递目标和 `EVENT_ID` 保序;某个目标失败时,不能跳过其队头投递后续事件。
|
|
|
|
|
|
|
|
|
|
|
|
`schd` 是最新状态通知,不逐条发送中间变化:统一由 `flushSchd` 按 `FLID` 聚合,取批次内最新事件后发送。它不提供逐条变更历史,不能与普通事件的 FIFO 语义混为一谈。
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 必须保持的约束
|
|
|
|
|
|
|
|
|
|
|
|
- **消息严格 FIFO**:队头失败并退避时,后续消息仍不能越过它。只有队头完成或按失败策略进入终态后,队列才继续推进。收报重扫和水位设计必须防止较小 ID 漏入队而被后续消息越过。
|
2026-09-09 17:53:08 +08:00
|
|
|
|
- **动态状态单写者**:`FLIGHT_SCHD` 及明细表只由主泵单线程写入。事务内第一步对 `PIPELINE_LOCK` 单行 `SELECT ... FOR UPDATE` 互斥;该方案在 PG/Oracle 11g 均无需数据库扩展或额外 DBA 特权。不能通过增加实例或处理线程直接扩容。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
- **身份去重**:同一业务身份只能绑定一条有效处理记录,重复报文不应再次产生业务副作用。具体身份组成和重放规则见设计文档。
|
2026-09-09 17:53:08 +08:00
|
|
|
|
- **快照可恢复**:快照以 `PROC_STATE` 成功终态判定重放(§5.1);每次成功写入推进 `STATE_VERSION`;`OPERATION_DAY` 一经确定不可变(§5.3)。
|
|
|
|
|
|
- **物理清除只发生在历史归档**:删除一律先标记(FDEL)或由生命周期清除;历史存储未接通时必须删 0 条(§8.2)。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
|
|
|
|
|
这些约束优先于吞吐量优化。单写者降低了并发复杂度,代价是队头阻塞和吞吐上限;如需并行化,必须先重新定义顺序与状态归属,不能只调整线程数。
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 数据归属与一致性
|
|
|
|
|
|
|
2026-09-07 16:12:00 +08:00
|
|
|
|
| 存储 | 承载内容 | 职责说明 |
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|---|---|---|
|
2026-09-09 17:53:08 +08:00
|
|
|
|
| 自有 PostgreSQL | 单行锁 `PIPELINE_LOCK`、处理状态 `PROC_STATE`、待发事件 `MSG_EVENT`、请求跟踪 `REQ_TRACK`、回填待办 `BACKFILL_TODO`、航班当前态 `FLIGHT_SCHD` + 9 张明细表、留痕 `SCHD_SNAP_LOG` | 本系统唯一业务数据库。消息处理、状态推进与待发事件在单事务内原子提交;本地事务只在此库。 |
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| 共享 MySQL | `CMINMSGS` 入站信箱、`COUTMSGS` 出站信箱 | 外部系统所有。仅执行约定的信箱读写和处理标记回填,不建表、不迁移 schema、不写历史表。兼容 HTTP 入口可按既有契约写入入站信箱。 |
|
|
|
|
|
|
**不使用跨库事务。** PG 事务只能保证“处理结果与待发事件一起提交”,不能覆盖 Redis 更新、MySQL 回填或 Kafka 发送。跨存储依靠幂等、重试和持久化补偿恢复:
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| 中断位置 | 恢复要求 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| 信箱已有报文,PG 入队失败 | 重扫补建,并按信箱 ID 去重。 |
|
2026-09-07 16:12:00 +08:00
|
|
|
|
| PG 提交失败 | 事务原子回滚,无中间态残留;消息重试时整体重放。 |
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| PG 已提交,信箱回填失败 | 持久化记录补偿任务并重试回填,不能重新执行已完成的业务处理。 |
|
|
|
|
|
|
| 下游已接收,本地尚未标记发送成功 | 允许重发;下游或出站适配协议必须具备去重能力。 |
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 16:12:00 +08:00
|
|
|
|
对外投递按**至少一次**设计,不承诺端到端恰好一次。Kafka 生产者幂等不能消除应用重启或 outbox 重发带来的所有重复。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 7. 关键决策索引
|
2026-09-07 10:27:44 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
保留 D1–D12 编号,便于设计文档和工程历史引用;以下是决策摘要,而非完成清单。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| 编号 | 决策及理由 |
|
|
|
|
|
|
|---|---|
|
2026-09-09 17:53:08 +08:00
|
|
|
|
| D1 | 业务报文严格 FIFO,优先保护航班状态的时序正确性;维护作业独立线程执行,不参与消息序。 |
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| D2 | 阶段 B 暂缓。历史写入成功后才可生成删除事件;顺序调用本身不保证原子性,恢复与去重方案需在启用前补齐。 |
|
|
|
|
|
|
| D3 | `schd` 只从 `flushSchd` 聚合发送,减少已被覆盖的中间状态通知。 |
|
|
|
|
|
|
| D4 | 未实现的报文类型按 `UNSUPPORTED` 可恢复失败处理,不当作非法报文直接丢弃;补齐能力后按重放规则恢复。 |
|
|
|
|
|
|
| D5 | 在持有具体消息或批次上下文的位置记录失败和退避;不吞掉线程中断或 JVM 严重错误。 |
|
|
|
|
|
|
| D6 | 基础设施通过接口注入,时间通过 `Clock` 注入,便于确定性测试顺序、重试与超时。 |
|
|
|
|
|
|
| D7 | 内存 stub 仅显式开启时装配,生产禁止使用,避免把未持久化的数据误当作已落库。 |
|
|
|
|
|
|
| D8 | 使用编译期依赖注入,并以启动冒烟测试验证关键 Bean 装配。 |
|
|
|
|
|
|
| D9 | 自有 PG 内完成本地事务,共享 MySQL 仅作信箱;跨存储采用补偿,不使用 XA。 |
|
|
|
|
|
|
| D10 | 动态状态单写者,生产只允许一个活动实例;多实例必须先具备可靠的排他保护。 |
|
|
|
|
|
|
| D11 | Kafka 生产要求 `acks=all`、`enable.idempotence=true`、`max.in.flight=1`,切流前验证 Broker 兼容性;不允许通过关闭幂等来满足生产接入。 |
|
|
|
|
|
|
| D12 | 仅将自有库终态记录归档到 `PROC_STATE_HST`,不侵入共享库的表结构或保留策略。 |
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 8. 部署、切换与运维
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
**部署与安全**
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-08 18:50:34 +08:00
|
|
|
|
- 生产维持单活动实例,停机时停止接收新任务并等待工作线程退出。已有事务级行锁,但消息认领和整个实例的排他保护尚未完成,不能依靠行锁宣称支持双实例 FIFO。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
- 配置、口令和环境端点通过环境变量提供。兼容写接口沿用内网信任模式,缺少鉴权,必须限制网络访问;管理端点不得直接暴露到生产外网。
|
|
|
|
|
|
- Eureka 用于服务发现,Logstash 接收结构化日志;日志出口故障不应阻塞业务处理。
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
**替换旧系统**
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-09 17:53:08 +08:00
|
|
|
|
采用“影子对拍 → 切流 → 旧系统冻结”。共享信箱不能让新旧系统同时认领和回填;影子输入使用只读水位或回放。影子环境须隔离 PG schema/实例、Kafka topic 和服务注册身份,并禁止误写生产信箱。切流时保证只有一个权威写者。
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
**可观测性要求**
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
使用消息 ID、事件 ID 关联处理与投递日志;健康检查反映依赖实际可用性,而不只是进程存活。运行中重点关注队列积压、队头滞留时间、投递延迟、重试/DEAD 数量和回填补偿积压。死信和一致性异常需要可执行的告警与重放流程,不能只留一条错误日志。
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 9. 当前实现与上线门槛
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
当前已有管道骨架、重试机制、部分 JDBC 适配和开发环境 stub 冒烟能力,**不能据此认定生产链路已闭环**。默认配置关闭管道自动启动及真实数据库/信箱适配。
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
上线前至少需要完成并验证:
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
- 真实 PG 事务、信箱水位与补扫、回填补偿、出站信箱,以及所需业务 Handler。
|
2026-09-09 17:53:08 +08:00
|
|
|
|
- `FLIGHT_SCHD` 事务原子性、`STATE_VERSION` 推进与 `OPERATION_DAY` 不可变校验、故障中断回滚恢复。
|
|
|
|
|
|
- FIFO、身份去重、FDEL/ADFT 生命周期、历史归档顺序和投递故障下的回归测试。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
- 生产启动校验、单实例排他保护、影子隔离和 Kafka 配置约束;当前配置仍允许 Kafka 参数覆盖,且默认 in-flight 值与 D11 要求不同。
|
|
|
|
|
|
- 死信告警、人工重放、端到端追踪、积压指标及安全边界。
|
2026-09-07 14:34:57 +08:00
|
|
|
|
|
2026-09-08 18:50:34 +08:00
|
|
|
|
具体实现差异见 design.md §10 与 ACM2-29 核查报告,工作由 Plane 跟踪。有效存储决策见 decision-flight-state.md;历史阶段口径不覆盖当前规则。
|