2026-09-13 20:31:13 +08:00
|
|
|
|
# 架构
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
本文写架构约束与归属,不代表能力已实现;上线与切流的操作步骤另立规程。旧版报文的格式兼容以 [SIS 接口规范](legacy/SIS_AODB_RMS-V0.1.md) 和 [XSD](legacy/unisysaodbsis.xsd) 为依据。
|
2026-09-15 13:23:40 +08:00
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 1. 系统定位与范围
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
msgexchange-v2 是 OMMS H5 查询系统的消息网关,替换旧版 `msgexchange-api`,收取 CIIMS adapter 信箱中 AODB 下发的 XML 报文:航班动态写入业务数据库、写 Redis 投影供页面查询,并经 Kafka 通知运营航班显示界面;静态参考数据(机场、航空公司、机型、机位等 13 类基础数据与资源状态)写入独立数据表。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
功能需求是 [requirements.md](requirements.md) 的十四条用户故事(采集、处理、投递、查询、维护,`US-01`~`US-14`),运行需求是四条验收(单实例、可观测、测试隔离、切换回退,`OPS-1`~`OPS-4`)。不生成航班/业务数据类报文,不替代 CIIMS/AODB,不调用 admin-api;完整非目标见同文件「范围与非目标」。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
- **主要入口**:轮询共享 MySQL 入站表 `CMINMSGS`,只取处理标记为空的行,按编号升序、每批有上限(`US-01`;`C-30`)。
|
|
|
|
|
|
- **兼容入口**:`POST /cminmsgs/send` 供联调工具把报文写进信箱,与上游投递走同一条处理路径;返回的编号只表示已进信箱,不代表已处理或下游已收到(`US-02`)。
|
|
|
|
|
|
- **查询入口**:`GET /all/flights` 返回当前全部动态航班(不含共享航班),读 Redis,与网页客户端同源(`US-12`;`INV-24`)。
|
2026-09-15 21:48:37 +08:00
|
|
|
|
- **出站**:只向 AODB 发参考数据请求 `RQRD` 和日计划请求 `RQFD`,经共享 MySQL 出站表 `COUTMSGS`,由 CIIMS adapter 消费;只保证请求写入信箱,不保证 AODB 收到(`US-09`;`C-24`)。
|
2026-09-15 13:23:40 +08:00
|
|
|
|
- **输出**:Kafka 主题 `msg` 发单条航班变更、`schd` 定时发批量最新状态;Redis 存航班投影;静态参考数据表供 admin-api 只读(`US-08`、`US-13`)。
|
2026-09-15 21:48:37 +08:00
|
|
|
|
- **权威**:航班当前态的权威是自有 PostgreSQL(`FLIGHT_SCHD`、资源明细表、`FLIGHT_ROUTE_POINT`);信箱、Redis、Kafka、展示视图都不是(`INV-11b`)。
|
2026-09-15 13:23:40 +08:00
|
|
|
|
- **航班历史**:已结束航班先写入 Elasticsearch 历史库,成功后才从实时数据删除(`US-14`;`D1`)。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
测试环境用 PostgreSQL;生产环境用 PostgreSQL 还是 Oracle 11g 尚未确定,Oracle 适配验证通过前不作支持承诺(生产库选型见 `Q1`)。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
|
|
|
|
|
## 2. 总体架构
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-09-15 13:23:40 +08:00
|
|
|
|
CIIMS adapter(把 AODB 下发的 XML 写入信箱)
|
|
|
|
|
|
│ 落信
|
2026-09-07 15:10:57 +08:00
|
|
|
|
▼
|
2026-09-15 13:23:40 +08:00
|
|
|
|
共享 MySQL:CMINMSGS(入站信箱)
|
|
|
|
|
|
│
|
2026-09-07 15:10:57 +08:00
|
|
|
|
▼
|
2026-09-15 13:23:40 +08:00
|
|
|
|
──────── msgexchange-v2(单活动实例,同进程四组线程) ────────
|
2026-09-15 21:48:37 +08:00
|
|
|
|
ingress 收报 processing 处理(流程见「主流程」)
|
|
|
|
|
|
delivery 投递 jobs 作业:回填 / 出站重试 / 历史清理 / 记录清理
|
2026-09-15 13:23:40 +08:00
|
|
|
|
──────────────────────────────────────────────
|
|
|
|
|
|
│
|
|
|
|
|
|
├─▶ Kafka:msg / schd ── 运营航班显示界面
|
|
|
|
|
|
├─▶ Redis:航班查询投影
|
|
|
|
|
|
├─▶ 共享 MySQL:COUTMSGS ── 参考数据与日计划请求发 AODB
|
|
|
|
|
|
├─▶ Elasticsearch:航班历史
|
2026-09-15 21:48:37 +08:00
|
|
|
|
└─▶ 自有 PostgreSQL:航班当前态 / 管道与未映射字段记录 / 静态参考数据 ── admin-api 只读参考数据
|
2026-09-06 22:30:03 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
四组线程在同一进程、互不调用,协作只经自有 PG 的持久记录交接;HTTP 接口走事件循环,不占这四组线程。重启后各段从记录接着做,不依赖内存进度(`US-01` AC4、`US-03` AC3、`US-10` AC2)。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
线程之间不加锁,靠幂等写入:收报按信箱编号只登记一次(`US-01` AC2)、回填只写空标记(`C-5`)、记录清理只删已回填且超过保留期的行(`INV-25`);发生竞争时后到的操作复查状态并重试。唯一的例外是处理消息的主循环(主泵)与航班历史清理之间要加锁(`INV-18`)。
|
2026-09-15 13:23:40 +08:00
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
运行边界:同一时刻只允许一个实例处理消息(`OPS-1`);切换与回退以信箱处理标记交接,本系统内部处理和回填进度保存在自有 PG 的 `PROC_STATE`,停旧启新时未处理的消息由旧系统继续(`OPS-4`);积压、处理失败、投递失败、回填失败各有指标与告警(`OPS-2`)。
|
2026-09-15 13:23:40 +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
|
|
|
|
| 模块 | 职责与边界 |
|
|
|
|
|
|
|---|---|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
| `ingress` | 轮询信箱、登记入队、兼容入口落信(`US-01`、`US-02`);不解析业务报文。 |
|
|
|
|
|
|
| `codec` | XML 解码,区分非法报文与可修复的解码失败(`US-03`)。 |
|
2026-09-15 21:48:37 +08:00
|
|
|
|
| `processing` | FIFO 调度、业务身份绑定与去重、领域决策与落库(`SCHD`/`FLOP`/`FDEL`/`ADFT`/静态参考数据),航班类写 Redis 投影,未落到航班当前态的字段写 `UNMAPPED_FIELD`。 |
|
2026-09-15 13:23:40 +08:00
|
|
|
|
| `delivery` | 读待发事件投 Kafka:按 `FLID` 保序、`schd` 聚合、失败重试(`US-08`;`C-29`)。 |
|
2026-09-15 21:48:37 +08:00
|
|
|
|
| `jobs` | 回填扫描、出站请求重试、航班历史清理,以及 `PROC_STATE`、`MSG_EVENT`、`SCHD_SNAP_LOG`、`REQ_TRACK` 的到期清理;单独线程、不进 FIFO(与主泵的互斥见 `INV-18`)。 |
|
2026-09-07 15:10:57 +08:00
|
|
|
|
| `domain` / `config` | 领域状态、事件和决策模型,以及运行参数。 |
|
2026-09-15 13:23:40 +08:00
|
|
|
|
| `infra` | 仓储(JDBC/stub)、外部适配器(共享信箱、Kafka、Redis、航班历史存储、AODB 出站)、重试、健康检查与日志;对其他模块只暴露接口。 |
|
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-15 13:23:40 +08:00
|
|
|
|
航班动态消息走满全链,其余类别只换其中几步:
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
1. **收报**:按「处理标记为空」发现信箱行,登记入队(`INV-2b`)。
|
|
|
|
|
|
2. **主泵**:按最小未完成 `MSG_ID` 取队头,解码,按业务身份去重(`INV-3`、`INV-9`);非法或不支持的报文无副作用,终态留档(`US-03`)。
|
2026-09-15 21:48:37 +08:00
|
|
|
|
3. **事务一**:持 `PIPELINE_LOCK`,领域变更、未映射字段记录与待发事件一起提交。
|
2026-09-15 13:23:40 +08:00
|
|
|
|
4. **投影**:写 Redis,写成功才算处理完成(`INV-23`);失败保持未完成、下轮重写投影,业务效果幂等(`US-03` AC3)。
|
|
|
|
|
|
5. **事务二**:处理终态与回填意图一起提交。
|
|
|
|
|
|
6. **回填**:作业把处理标记写回共享信箱(`US-10`);写不上的记录在案并告警。
|
|
|
|
|
|
7. **投递**:读待发事件发 `KAFKA:msg` / `KAFKA:schd`,至少一次、`FLID` 内保序(`INV-10`、`C-29`);一直失败的记录保留可查并告警(`US-08`)。
|
|
|
|
|
|
|
|
|
|
|
|
其余类别与这条主干的差异:
|
|
|
|
|
|
|
|
|
|
|
|
| 类别 | 与主干的差异 | 完成判据 | 失败时 |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| 日计划(`DNLD`/`RESP`) | 第 3 步改为每批一个事务,第 5 步在整包完成后 | 整包成功,含 Redis 刷新(`US-07` AC4/AC5) | 不标记已处理,下轮整包重处理(`US-07` AC4) |
|
|
|
|
|
|
| 静态参考数据 | 无第 4 步;一个事务完成落库、终态与回填意图 | 该类落库成功(`US-13` AC2) | 校验不过整类不动,其他类照常(`US-13` AC2) |
|
2026-09-15 21:48:37 +08:00
|
|
|
|
| 出站请求 | 不走收报队列:登记新请求并作废同子类型旧请求;`RQRD` 与 `RQFD` 各自最多一条在途,后续请求待前一条结案再写入 `COUTMSGS` | 请求已写入信箱(`US-09` AC1) | `jobs` 按 `REQ_TRACK` 重试仍有效且确认未落信的请求;写入结果不明时记录并告警,不直接重发;交付承诺只到落信(`C-24`) |
|
2026-09-15 13:23:40 +08:00
|
|
|
|
| 航班历史清理(作业) | 不走消息队列:历史写入成功后物理删除 | 实时数据已删(`US-14` AC3) | 历史写不成功不删,下轮重来(`US-14` AC3) |
|
|
|
|
|
|
|
|
|
|
|
|
出站的后半程:应答按报文类型匹配等待中的请求,发错或迟到的不更新数据、记录后跳过;超过时限未等到应答,标记超时;`EROR` 定位到本系统发出的请求,标记失败并告警(`US-09`)。
|
|
|
|
|
|
|
2026-09-07 15:10:57 +08:00
|
|
|
|
## 5. 必须保持的约束
|
|
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
以下约束不得违反:
|
2026-09-11 15:48:14 +08:00
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
- 只跑一个实例;主泵处理器写航班当前态,历史清理作物理删除,两者互斥;信箱读取不加锁(`OPS-1`、`PRE-5`、`INV-18`)。
|
2026-09-15 13:23:40 +08:00
|
|
|
|
- 按顺序处理、只处理一次:每次只取编号最小的未完成消息,重复扫描、失败重处理与兼容入口并发都只登记一次、生效一次(`INV-2b`、`INV-3`、`INV-9`);不丢消息依赖「编号即到达顺序」且编号不复用、不回退(`PRE-2`、`PRE-3`)。
|
|
|
|
|
|
- Redis 写成功才算处理完成;查询接口与网页客户端读同一份 Redis,出问题时报错,不返回空列表假装正常(`INV-23`、`INV-24`;`US-05`、`US-06`、`US-12`)。
|
|
|
|
|
|
- 日计划快照以 AODB 下发为准:快照里没有的航班删除,未携带的字段清除;增量报文不适用这条(`INV-14b`、`INV-15b`;`US-07`)。
|
|
|
|
|
|
- 对外投递至少一次,同一航班(`FLID`)内保序,跨航班不承诺顺序(`INV-10`、`C-29`;`US-08`)。
|
|
|
|
|
|
- 航班只在历史写入成功后删除,历史库还没接通时一条也不删(`D1`、`INV-28`;`US-14`)。
|
|
|
|
|
|
- 静态参考数据一类校验失败只停这一类,其他类照常;空值是「当前没有值」,不是删除(`INV-26`、`INV-27`;`US-13`)。
|
|
|
|
|
|
- 航班唯一,版本只进不退:`FLID` 唯一,已写入非空的运营日不可改;每次成功写入版本号加一,重复消息不重复加(`INV-12`、`INV-13`)。
|
2026-09-07 15:10:57 +08:00
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
这些约束是拿速度换来的:单活动主泵一次只推进一条,前一条没处理完,后面都得等。要提速、要多实例,光加线程没有用——得先重新设计消息顺序和数据由谁写,多实例还得补上可靠的互斥保护。
|
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-15 21:48:37 +08:00
|
|
|
|
| 自有 PostgreSQL | 处理锁 `PIPELINE_LOCK`、消息处理状态与回填意图 `PROC_STATE`、待发事件 `MSG_EVENT`、出站请求跟踪 `REQ_TRACK`、航班当前态 `FLIGHT_SCHD`、资源明细表、`FLIGHT_ROUTE_POINT`、未映射字段长期记录表 `UNMAPPED_FIELD`、静态参考数据 `REF_MASTER`、日计划快照留痕 `SCHD_SNAP_LOG` | 本系统唯一的业务数据库,也是航班当前态的唯一权威(`INV-11b`);本地事务只发生在这里,事务怎么分段见「主流程」。`UNMAPPED_FIELD` 不随 `PROC_STATE` 到期清理。 |
|
2026-09-15 13:23:40 +08:00
|
|
|
|
| Redis | 航班查询投影 | 只作查询,不是权威,也不存处理状态(`INV-11b`);只由本系统写入和移除(`INV-24`),内容来自 PG 当前态;`GET /all/flights` 与网页客户端读的就是它。 |
|
|
|
|
|
|
| 共享 MySQL | `CMINMSGS` 入站信箱、`COUTMSGS` 出站信箱 | 信箱归外部系统所有。本系统只读写消息、回写处理标记,不建表、不改表结构、不清数据、不写历史表(`C-14`);原文保留多久、何时清除由库方定(`C-5`~`C-12`、`Q7`、`Q9`)。出站请求写进去就算交付(`C-24`)。 |
|
|
|
|
|
|
| 航班历史存储(Elasticsearch) | 已结束航班的历史副本 | 已结束航班写入这里作历史副本;写入确认成功后才删实时数据,写不成一条也不删(`D1`、`INV-28`)。保留期与容量上限未定(`G-FLIGHT-HIST-RETENTION`)。 |
|
2026-09-10 08:16:31 +08:00
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
**PG 的事务只管自己库。** Redis 写没写成、信箱标记写没写上、Kafka 发没发出,PG 事务都管不着;这些步骤各自可重试,重做多少遍结果都一样,重启后从 PG 记录接着走。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-15 13:23:40 +08:00
|
|
|
|
对外投递只承诺至少一次(`C-29`):应用重启、待发事件重发都可能让同一条消息多发一次,Kafka 的生产端幂等挡不住这种重复。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-09 21:50:05 +08:00
|
|
|
|
## 7. 关键决策
|
2026-09-07 10:27:44 +08:00
|
|
|
|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
只列正文推不出来、仍有约束价值的决策,按 `D1`–`D2` 编号。第三列只给证据与偏差指针;决策不随实现状态增删。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-13 20:31:13 +08:00
|
|
|
|
| 编号 | 决策及理由 | 证据 / 偏差 |
|
2026-09-09 21:50:05 +08:00
|
|
|
|
|---|---|---|
|
2026-09-15 21:48:37 +08:00
|
|
|
|
| D1 | 删除实时数据前,须已登记待发删除事件:FDEL 在航班状态变更事务中登记,Redis 移除成功后才算处理完成;历史清理在历史写入成功后,必要时登记待发删除事件,再物理删除。历史写入是需求内交付(`US-14`)。 | `US-06` AC1、`US-14` AC3 |
|
|
|
|
|
|
| D2 | Kafka 生产端同时满足三项:确认级别、幂等、单连接在途条数上限;不许关幂等绕开这条限制。 | 保序目标见 `US-08` AC2 |
|