Files
msgexchange-v2/docs/architecture.md
T
windyboy 879d658159 refactor(flight-state): 按 flight-state.md 审计定稿全量重构脚手架与 SQL (ACM2-31)
- SQL 基线 V1__flight_state_baseline.sql 整体取代 V1.0.0–V1.4.0:
  PIPELINE_LOCK/PROC_STATE/MSG_EVENT/REQ_TRACK/BACKFILL_TODO/FLIGHT_SCHD
  + 8 张资源明细表 + FLIGHT_ROUTE_POINT + SCHD_SNAP_LOG 留痕层
- 废除 FDAY 日代/SCHD_GEN/名单差删:OPERATION_DAY 不可变(应用层校验 +
  条件更新强化 §7.4),STATE 仅 ACTIVE/DELETED,物理清除只在历史归档后
- 处理器化:applyScheduleRecords(§5.1 七步同一事务,重放判定/整包
  DEAD(PROTOCOL)/归属冲突不落地)+ FLOP/FDEL/ADFT(tombstone 仅
  ACTIVE→DELETED,重复 FDEL 幂等不推进版本)
- 投递:KAFKA_SCHD 同 FLID 按最新 STATE_VERSION 合并,被压掉事件关闭,
  TOMBSTONE 发 null 值消息(键缺失=删除旧值 §7.3)
- 回填待办改为业务事务内预登记,消除提交后写待办的崩溃窗口(§7.2/§10)
- XML 解码改为 jackson-dataformat-xml 数据类直接映射(SIS 信封强类型,
  FLTR 开放标签泛型承载)
- 历史归档/物理清除顺序不可颠倒:归档确认成功集才物理删除,未接通删 0 条
- 移除 PUMP_JOB 队列/ReferenceService/FlightStoreDiffTool 等旧机制与测试,
  新增运营日/引擎/快照/FDEL/归档顺序不变性回归测试
2026-09-09 17:53:08 +08:00

151 lines
12 KiB
Markdown
Raw Blame History

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