Files
msgexchange-v2/docs/architecture.md
T
windyboy 7e3bf383a5 docs+fix: 文档回到代码事实;修复 dev 冒烟使其真正可跑(ACM2-10 复核意见落地)
实测过程发现并修复四层问题,随后全部按实测结果校订文档:

- build.gradle.kts:补 application.mainClass(Kotlin 顶层 main → ApplicationKt)。
  此前 ./gradlew run 报 "No main class specified",installDist 启动脚本主类为空(Dockerfile 不可运行)。
- application-dev.yml:stub 模式真正免基础设施——
  * datasources.default.enabled=false(micronaut-jdbc JdbcDataSourceEnabled 条件排除 DataSource,
    原注释"stub 不建连"意图未实现,DataSource eager 强制连 MySQL 导致启动失败);
  * 关闭 micronaut 自带 Redis/Kafka 健康指示器(无 broker 时装配即把 /health 打成 500);
  * 端点配置前缀修正:micronaut.endpoints.* 是死配置(/env 404、/beans 401 实测),
    正确为顶层 endpoints.*(env 默认禁用 / beans 默认 enabled+sensitive 一并注明);
  * 关闭 eureka discovery,避免无注册中心时 /health 变 DOWN。
  验证:MICRONAUT_ENVIRONMENTS=dev ./gradlew run 无任何 env/外部依赖启动 → POST /cminmsgs/send
  200 返回 ID → /health UP;text/plain 415(U16 缺口实证)。37 测试全绿未受影响。
- README/docs/architecture.md/docs/design.md:按实测与复核意见校订——
  * README:dev 冒烟命令恢复为"无需 DB/Redis/Kafka",记录修复缘由;
  * architecture:U17 服务名未落地(注册名仍取 micronaut.application.name=msgexchange-nextgen,
    msgx.service-name 无消费方)、三层隔离为目标态(仅 register-eureka=false 生效)、
    管理端点 5.1 实际口径(/env 默认禁用、/beans 默认 enabled+sensitive)、就绪度口径更新;
  * design:§9 补 U17 行;smoke 路径 UNSUPPORTED→CODEC_ERROR 修正;毒丸 head-deadline 判据不可达、
    快照 CAS 崩溃重放二次自增版本等"代码行为≠文档声明"处加实现注;I1 状态行同步。
- gradle/libs.versions.toml + gradle.properties:版本矩阵注释按依赖解析实测修正
  (platform 5.1.3 生效但 core 系解析 5.1.13,classpath 混用;原"5.1.10 无平台 BOM"说法与观测不符)。

未提交内容:无。代码级缺陷(毒丸判据、CAS 重放、DNLD 路由顺序、identity 键释放等)仍属
ACM2-10 排期,本次仅按文档职责如实标注,未改动实现。
2026-09-07 07:39:57 +08:00

11 KiB
Raw Blame History

msgexchange-v2 架构文档

现行架构权威为 Plane airport_chengdu_msgexchange_api 工作区的 ACM2-3(综合架构 v4 脚手架跟踪 ACM2-4,评审与实施计划(U01U30ACM2-10。本文是仓库内的架构速览, 与代码同步维护;两者冲突时以 ACM2-3 为准并回改本文。 配套设计细节见 design.md

1. 系统定位

新一代机场消息交换服务(AODB 报文接入 → 处理 → 对外投递),替换 legacy msgexchange-apiJava 8 / Spring Boot 1.5 / Maven)。过渡策略为双跑三步

影子对拍(同入口双收,比对输出)→ 切流(nextgen 权威)→ 旧仓库冻结
  • legacy 维护不受本仓库影响;本仓库不声明 legacy 旧表 schema(见 §6 数据边界)。
  • wire 契约冻结:消息结构唯一事实源为 SIS_AODB_RMS-V0.1.md + doc/unisysaodbsis.xsd; HTTP 端点路径与响应语义沿用现役(如 POST /cminmsgs/send 返回记录 ID)。

2. 技术栈

选型 说明
语言/运行时 Kotlin 2.3 + JDK 25 JDK 21 不可行(Micronaut 5.1 系要求 JVM 25+ACMA-9 实测)
框架 Micronaut 5.1.3 编译期 DIKSPkotlin-ksp + micronaut-inject-kotlin)生成 *$Definition
持久化 MySQL + Flyway 仓储现为接口(Micronaut Data JDBC 实装属 U05,阶段 1 后续)
权威存储 Redis(阶段 A flightInfo hash;仅主泵线程写(I5);Lua 脚本原子覆盖
投递 Kafkaacks=all + 幂等) outbox 模式,经 MSG_EVENT 表中转
投影(阶段 B Elasticsearch + Redis 投影 + FLIGHT_STATE 仅阶段 B 启用(msgx.phase
注册中心 EurekaMicronaut 原生键) 服务名契约 msgexchangeapi(影子 msgexchangeapi-shadow)——U17 未落地:当前注册名仍取 micronaut.application.name=msgexchange-nextgen),msgx.service-name 无运行时消费方(见 §8 与 design.md §9
可观测 logstash TCPAsync 包装)+ MDC traceId + 自定义健康指示器 design.md §8

3. 总体拓扑

                    ┌──────────────────────────────────────────────────┐
                    │                msgexchange-nextgen               │
                    │                (单实例 · 单写者)                │
 AODB/上游 ──HTTP──▶│ ingress                                          │
                    │   InboxService ──事务1──▶ CMINMSGS(原文)        │
                    │                        └▶ PROC_STATEPENDING  │
                    │                                                  │
                    │ processingmsgx-pump 线程,严格 FIFO 队头)       │
                    │   Pump ──tick──▶ MessageProcessor                │
                    │      │                │ decodeXmlCodec        │
                    │      │                │ identity 绑定(I3       │
                    │      │                │ Handler.decide(纯函数)   │
                    │      │─Schd DNLD──▶ SnapshotFlow(流程4         │
                    │      │─PUMP_JOB───▶ JobExecutor(同队列,决策1   │
                    │      │                                           │
                    │      ├────Redis Lua──▶ Redis flightInfoA权威)  │
                    │      └──事务2──▶ MSG_EVENToutbox+ 回填+SUCCEEDED│
                    │                                                  │
                    │ deliverymsgx-dispatcher 线程,每 target FIFO   │
                    │   Dispatcher ──逐条──▶ Kafka(msg)                │
                    │      └─flushSchd 聚合─▶ Kafka(schd)              │
                    │     (阶段 B 追加:ES flight_hts → Redis 投影删除) │
                    └──────────────────────────────────────────────────┘
                                        │                │
                                        ▼                ▼
                                   下游 Kafka topic    Eureka / logstash

要点:

  • 两条专用 daemon 单线程msgx-pump / msgx-dispatcher)由 PipelineLifecycleServerStartupEvent 后拉起,不占用 Netty event loop;停机 requestStop + interrupt + joinU07)。仅当 msgx.pipeline.autostart=true 时装配——生产默认关, 当前属有意脚手架门禁(生产可运行需先完成 U05 数据层实装,见 §7)。
  • 单写者约束(I5:阶段 A 全部 Redis 写集中在主泵线程;实例数必须为 1 (运行期租约/选主保护属 U26,尚未实装,当前靠部署拓扑约束)。
  • 统一 FIFO(决策 1:定时作业(cron → PUMP_JOB 入队)与消息同队列, 作业产物不绕过队头顺序;job 与队头的先后目前为入队时间近似, 统一序号列属 U15(未实装,见 design.md §9 缺口清单)。

4. 模块职责

职责 对应 ACMA-8 主要类
ingress/ 收报事务1:原文落库 + 伴生 PENDING 行;不解析报文 流程 1I3 InboxController InboxService
processing/ 主泵:FIFO 领取、解码、identity 绑定、纯函数决策、事务2 流程 2/4I1/I2/I5 Pump MessageProcessor SnapshotFlow Identity Handler(Registry)
delivery/ 投递:每 target 严格 FIFO、schd 聚合 流程 3 Dispatcher SchdAggregation
jobs/ 泵作业:清场/归档/投影重建(经 PUMP_JOB 同队列) 流程 4/5/7I4 JobExecutor HistorySweepJob ArchiveJob ProjectionRebuildJob
codec/ XML 解码 + 失败分类(MALFORMED vs CODEC_ERROR 决策 4 前置 XmlCodec DecodeResult
domain/ 状态机枚举、事件/决策模型、Phase 开关 I1I5 ProcState MsgEvent Decision MsgKind
infra/ 仓储接口、重试策略、Redis Lua、stub、健康、日志 数据模型节 见 design.md
config/ PipelineProps 参数表(ACMA-8 参数初值) PipelineProps

5. 关键架构决策

# 决策 落点
D1 作业与消息同队列(cron 只经 PUMP_JOB 入队,产物不绕过队头) Pump.tick / JobExecutor
D2 阶段 BES 投递成功后同线程同步 enqueue 删除事件(不轮询 ack Dispatcher.tick(定案 2
D3 schd 唯一出口是 flushSchd 批量聚合(逐条循环显式排除 KAFKA_SCHD) Dispatcher.tickU06/N03
D4 未实装 ≠ 非法:无 handler / staging 未实装 → FAILED(UNSUPPORTED) 可重放,绝不写终态 MessageProcessor SnapshotFlowU10/N21
D5 失败迁移在持有具体 head/batch 的边界完成;loop 只作最后防线,不吞 InterruptedException/Error MessageProcessor DispatcherU08
D6 接口驱动 + 假仓储单测;时间一律经可注入 Clock infra/persistence FailureScheduler
D7 stub 装配门禁:msgx.stubs=true 才装配内存实装;与 autostart 组合支撑 dev 冒烟 infra/stubU07/U01
D8 编译期 DI(KSP)+ 启动期冒烟测试锁定 BeanDefinition 生成 build.gradle.ktsU01

6. 数据边界

  • 本仓库 Flyway 只建六张辅助表PROC_STATE / MSG_EVENT / REF_DATA / REQ_TRACK / PUMP_JOB / FLIGHT_STATEV2.0.0__aux_tables.sql)。
  • CMINMSGS / CMINMSGS_HST / COUTMSGS 等 legacy 旧表归 legacy 仓库维护(冻结期), 本仓库不重复声明;全新空库需先建 legacy schema,否则收报首句 SQL 报表不存在 (README「数据库初始化」节)。
  • 回滚兼容关键:SUCCEEDED 时回填 CMINMSGS.SUBSYSTEM_* + DATE_PROCESSED/STATUS, 旧系统可按自身语义无缝接管(Runbook 第 7 步)。

7. 两阶段权威与当前就绪度

阶段 权威 投递目标 状态
Amsgx.phase=A Redis flightInfo KAFKA:msg、KAFKA:schd 管道骨架+重试闭环已实装;Redis Lua/实仓储属 U05/U09
Bmsgx.phase=B FLIGHT_STATE + 投影 + ES:flight_hts、REDIS:flightInfo 未实施(阶段 2 后)

就绪度(2026-09-07 复核口径):可编译、37 测试全绿、dev stub 进程级冒烟实测可端到端 ./gradlew run 无外部依赖启动 → 收报 200 → /health UP,修复记录见 README「进程级 dev 冒烟」); 生产默认配置不可对外服务——autostart=false 且生产(stubs=false)下仓储无实装、DI 装配 即失败。生产就绪前置:U05(数据层+事务)、U07 fail-fast 定案、U09(快照恢复协议)、 U13(投递毒丸补全)、U15(统一序号)。逐项状态见 ACM2-10「定稿实施计划」。

8. 部署与安全姿态

  • 实例数 = 1(主泵单写者前提);双实例误配当前无运行期防护(U26:租约/DB 锁 + 拒启,未实装)。
  • 影子隔离(目标态;U17/U26 未落地,勿按现状引用):服务名(msgexchangeapi-shadow+ 独立 schema + Redis key 前缀 + 独立 topic 三层隔离;当前代码仅 msgx.register-eureka=false 生效—— Kafka topic 写死字面量 "msg"/"schd"Dispatcher)、FlightRedisClient.eval 无 key 前缀参数、 服务名未接 msgx.service-name(§2)。
  • 网络信任模型/cminmsgs/send 无鉴权(沿用现役内网信任姿态);eureka default-zone 回退 127.0.0.1:8761;口令/端点全部环境变量外置(零入库)。安全节细化属 U28。
  • 管理端点Micronaut 5.1 下 /env 默认禁用/beans 默认 enabled+sensitivedev/影子经 顶层 endpoints.* micronaut.endpoints.*——实测前缀错误时不生效)放开 /env/beans 与 health 明细。工程未引入 micronaut-securitysensitive 的实际拦截行为待 U28 定案。
  • 同名单风险:影子与生产同名同路径会互相收报——切流前必须核对服务名三隔离。

9. 可观测性

  • 日志logstash TCP JSON 通道(Async + neverBlock 降级,logstash 不可达不阻塞业务线程); 结构化生命周期日志(收报/SUCCEEDED/SKIPPED/FAILED/DEAD/毒丸/flush 批次);MDC traceId (当前 = cminmsgsId/eventId,处理片段;贯穿收报→投递属 U12 遗留)。
  • 健康/health 聚合 redis-flight-store / kafka-delivery 自定义指示器——真实 ping 判定(false/异常→DOWN,缺 bean→DOWN),非仅 bean 存在。
  • 指标缺口micrometer 队列深度/投递延迟 gauge 未引入(版本对齐待 U05 批次); DEAD/DLQ 告警出口与一致性哨兵实装(U25)未落地——告警当前以 ERROR 日志为落点。