Files
msgexchange-v2/docs/architecture.md
T

12 KiB
Raw Blame History

架构

本文写架构约束与归属,不代表能力已实现;上线与切流的操作步骤另立规程。旧版报文的格式兼容以 SIS 接口规范XSD 为依据。

1. 系统定位与范围

msgexchange-v2 是 OMMS H5 查询系统的消息网关,替换旧版 msgexchange-api,收取 CIIMS adapter 信箱中 AODB 下发的 XML 报文:航班动态写入业务数据库、写 Redis 投影供页面查询,并经 Kafka 通知运营航班显示界面;静态参考数据(机场、航空公司、机型、机位等 13 类基础数据与资源状态)写入独立数据表。

功能需求是 requirements.md 的十四条用户故事(采集、处理、投递、查询、维护,US-01US-14),运行需求是四条验收(单实例、可观测、测试隔离、切换回退,OPS-1OPS-4)。不生成航班/业务数据类报文,不替代 CIIMS/AODB,不调用 admin-api;完整非目标见同文件「范围与非目标」。

  • 主要入口:轮询共享 MySQL 入站表 CMINMSGS,只取处理时间为空的行,按编号升序、每批有上限(US-01INV-2b)。
  • 兼容入口POST /cminmsgs/send 供联调工具把报文写进信箱,与上游投递走同一条处理路径;返回的编号只表示已进信箱,不代表已处理或下游已收到(US-02)。
  • 查询入口GET /all/flights 返回当前全部动态航班(不含共享航班),读 Redis,与网页客户端同源(US-12INV-24)。
  • 出站:只向 AODB 发参考数据请求 RQRD 和日计划请求 RQFD,经共享 MySQL 出站表 COUTMSGS,由 CIIMS adapter 消费;只保证请求写入信箱,不保证 AODB 收到(US-09C-24)。
  • 输出Kafka 主题 msg 发单条航班变更、schd 定时发批量最新状态;Redis 存航班投影;静态参考数据表供 admin-api 只读(US-08US-13)。
  • 权威:航班当前态的权威是自有 PostgreSQL(FLIGHT_SCHD、资源明细表、FLIGHT_ROUTE_POINT);信箱、Redis、Kafka、展示视图都不是(INV-11b)。
  • 航班历史:已结束航班先写入 Elasticsearch 历史库,成功后才从实时数据删除(US-14D1)。

测试环境用 PostgreSQL;生产环境用 PostgreSQL 还是 Oracle 11g 尚未确定,Oracle 适配验证通过前不作支持承诺(生产库选型见 Q1)。

2. 总体架构

CIIMS adapter(把 AODB 下发的 XML 写入信箱)
        │ 落信
        ▼
共享 MySQLCMINMSGS(入站信箱)
        │
        ▼
──────── msgexchange-v2(单活动实例,同进程四组线程) ────────
ingress 收报      processing 处理(流程见「主流程」)
delivery 投递     jobs       作业:回填 / 出站重试 / 历史清理 / 记录清理
──────────────────────────────────────────────
        │
        ├─▶ Kafkamsg / schd ── 运营航班显示界面
        ├─▶ Redis:航班查询投影
        ├─▶ 共享 MySQL:COUTMSGS ── 参考数据与日计划请求发 AODB
        ├─▶ Elasticsearch:航班历史
        └─▶ 自有 PostgreSQL:航班当前态 / 管道与未映射字段记录 / 静态参考数据 ── admin-api 只读参考数据

四组线程在同一进程、互不调用,协作只经自有 PG 的持久记录交接;HTTP 接口走事件循环,不占这四组线程。重启后各段从记录接着做,不依赖内存进度(US-01 AC4、US-03 AC3、US-10 AC2)。

线程之间不加锁,靠幂等写入:收报按信箱编号只登记一次(US-01 AC2)、回填只写空标记(C-15)、记录清理只删已回填且超过保留期的行(INV-25);发生竞争时后到的操作复查状态并重试。唯一的例外是处理消息的主循环(主泵)与航班历史清理之间要加锁(INV-18)。

运行边界:同一时刻只允许一个实例处理消息(OPS-1);切换与回退以信箱处理标记交接,本系统内部处理和回填进度保存在自有 PG 的 PROC_STATE,停旧启新时未处理的消息由旧系统继续(OPS-4);积压、处理失败、投递失败、回填失败各有指标与告警(OPS-2)。

技术栈:Kotlin + JDK 25、Micronaut 编译期依赖注入、JDBC 持久化;数据库变更由 Flyway 管理,只作用于自有 PostgreSQL。依赖版本见 build.gradle.kts

3. 模块职责

模块 职责与边界
ingress 轮询信箱、登记入队、兼容入口落信(US-01US-02);不解析业务报文。
codec XML 解码,区分非法报文与可修复的解码失败(US-03)。
processing FIFO 调度、业务身份绑定与去重、领域决策与落库(SCHD/FLOP/FDEL/ADFT/静态参考数据),航班类写 Redis 投影,未落到航班当前态的字段写 UNMAPPED_FIELD
delivery 读待发事件投 Kafka:按 FLID 保序、schd 聚合、失败重试(US-08C-29)。
jobs 回填扫描、出站请求重试、航班历史清理,以及 PROC_STATEMSG_EVENTSCHD_SNAP_LOGREQ_TRACK 的到期清理;单独线程、不进 FIFO(与主泵的互斥见 INV-18)。
domain / config 领域状态、事件和决策模型,以及运行参数。
infra 仓储(JDBC/stub)、外部适配器(共享信箱、Kafka、Redis、航班历史存储、AODB 出站)、重试、健康检查与日志;对其他模块只暴露接口。

4. 主流程

航班动态消息走满全链,其余类别只换其中几步:

  1. 收报:按「处理时间为空」发现信箱行,登记入队(INV-2b)。
  2. 主泵:按最小未完成 MSG_ID 取队头,解码,按业务身份去重(INV-3INV-9);非法或不支持的报文无副作用,终态留档(US-03)。
  3. 事务一:持 PIPELINE_LOCK,领域变更、未映射字段记录与待发事件一起提交。
  4. 投影:写 Redis,写成功才算处理完成(INV-23);失败保持未完成、下轮重写投影,业务效果幂等(US-03 AC3)。
  5. 事务二:处理终态与回填意图一起提交。
  6. 回填:作业把处理标记写回共享信箱(US-10);写不上的记录在案并告警。
  7. 投递:读待发事件发 KAFKA:msg / KAFKA:schd,至少一次、FLID 内保序(INV-10C-29);一直失败的记录保留可查并告警(US-08)。

其余类别与这条主干的差异:

类别 与主干的差异 完成判据 失败时
日计划(DNLD/RESP 第 3 步改为每批一个事务,第 5 步在整包完成后 整包成功,含 Redis 刷新(US-07 AC4/AC5 不标记已处理,下轮整包重处理(US-07 AC4
静态参考数据 无第 4 步;一个事务完成落库、终态与回填意图 该类落库成功(US-13 AC1/AC5 校验不过整类不动,其他类照常(US-13 AC2
出站请求 不走收报队列:登记新请求并作废同子类型旧请求;RQRDRQFD 各自最多一条在途,后续请求待前一条结案再写入 COUTMSGS 请求已写入信箱(US-09 AC1 jobsREQ_TRACK 重试仍有效且确认未落信的请求;写入结果不明时记录并告警,不直接重发;交付承诺只到落信(C-24
航班历史清理(作业) 不走消息队列:历史写入成功后物理删除 实时数据已删(US-14 AC3 历史写不成功不删,下轮重来(US-14 AC3

出站的后半程:应答按报文类型匹配等待中的请求,发错或迟到的不更新数据、记录后跳过;超过时限未等到应答,标记超时;EROR 定位到本系统发出的请求,标记失败并告警(US-09)。

5. 必须保持的约束

以下约束不得违反:

  • 只跑一个实例;主泵处理器写航班当前态,历史清理作物理删除,两者互斥;信箱读取不加锁(OPS-1PRE-5INV-18)。
  • 按顺序处理、只处理一次:每次只取编号最小的未完成消息,重复扫描、失败重处理与兼容入口并发都只登记一次、生效一次(INV-2bINV-3INV-9);不丢消息依赖「编号即到达顺序」且编号不复用、不回退(PRE-2PRE-3)。
  • Redis 写成功才算处理完成;查询接口与网页客户端读同一份 Redis,出问题时报错,不返回空列表假装正常(INV-23INV-24US-05US-06US-12)。
  • 日计划快照以 AODB 下发为准:快照里没有的航班删除,未携带的字段清除;增量报文不适用这条(INV-14bINV-15bUS-07)。
  • 对外投递至少一次,同一航班(FLID)内保序,跨航班不承诺顺序(INV-10C-29US-08)。
  • 航班只在历史写入成功后删除,历史库还没接通时一条也不删(D1INV-28US-14)。
  • 静态参考数据一类校验失败只停这一类,其他类照常;空值是「当前没有值」,不是删除(INV-26INV-27US-13)。
  • 航班唯一,版本只进不退:FLID 唯一,已写入非空的运营日不可改;每次成功写入版本号加一,重复消息不重复加(INV-12INV-13)。

这些约束是拿速度换来的:单活动主泵一次只推进一条,前一条没处理完,后面都得等。要提速、要多实例,光加线程没有用——得先重新设计消息顺序和数据由谁写,多实例还得补上可靠的互斥保护。

6. 数据归属与一致性

存储 承载内容 职责说明
自有 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 到期清理。
Redis 航班查询投影 只作查询,不是权威,也不存处理状态(INV-11b);只由本系统写入和移除(INV-24),内容来自 PG 当前态;GET /all/flights 与网页客户端读的就是它。
共享 MySQL CMINMSGS 入站信箱、COUTMSGS 出站信箱 信箱归外部系统所有。本系统只读写消息、回写处理标记,不建表、不改表结构、不清数据、不写历史表(C-14);原文保留多久、何时清除由库方定(C-6C-9Q7Q9)。出站请求写进去就算交付(C-24)。
航班历史存储(Elasticsearch 已结束航班的历史副本 已结束航班写入这里作历史副本;写入确认成功后才删实时数据,写不成一条也不删(D1INV-28)。保留期与容量上限未定(G-FLIGHT-HIST-RETENTION)。

PG 的事务只管自己库。 Redis 写没写成、信箱标记写没写上、Kafka 发没发出,PG 事务都管不着;这些步骤各自可重试,重做多少遍结果都一样,重启后从 PG 记录接着走。

对外投递只承诺至少一次(C-29):应用重启、待发事件重发都可能让同一条消息多发一次,Kafka 的生产端幂等挡不住这种重复。

7. 关键决策

只列正文推不出来、仍有约束价值的决策,按 D1D2 编号。第三列只给证据与偏差指针;决策不随实现状态增删。

编号 决策及理由 证据 / 偏差
D1 删除实时数据前,须已登记待发删除事件:FDEL 在航班状态变更事务中登记,Redis 移除成功后才算处理完成;历史清理在历史写入成功后,必要时登记待发删除事件,再物理删除。历史写入是需求内交付(US-14)。 US-06 AC1、US-14 AC3
D2 Kafka 生产端同时满足三项:确认级别、幂等、单连接在途条数上限;不许关幂等绕开这条限制。 保序目标见 US-08 AC2