From dc68f1e1f8745b32aafd33ea89730c6a0298d373 Mon Sep 17 00:00:00 2001 From: windyboy Date: Mon, 7 Sep 2026 15:11:06 +0800 Subject: [PATCH] =?UTF-8?q?chore(infra):=20=E5=A2=9E=E5=8A=A0=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E4=B8=AD=E9=97=B4=E4=BB=B6=E7=BC=96=E6=8E=92=E3=80=81?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E9=85=8D=E7=BD=AE=E4=B8=8E=20Kafka=20?= =?UTF-8?q?=E9=99=8D=E7=BA=A7=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 27 +++++++ compose.yaml | 104 +++++++++++++++++++++++++ deploy/dev/mysql-init/01-mailbox.sql | 66 ++++++++++++++++ src/main/resources/application-dev.yml | 21 +++-- src/main/resources/application.yml | 9 ++- 5 files changed, 217 insertions(+), 10 deletions(-) create mode 100644 .env.example create mode 100644 compose.yaml create mode 100644 deploy/dev/mysql-init/01-mailbox.sql diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6a1af9c --- /dev/null +++ b/.env.example @@ -0,0 +1,27 @@ +# ===================================================================== +# msgexchange-v2 本地开发环境变量示例(ACM2-13 口径) +# 复制为 .env 或直接在本地启动时 export +# ===================================================================== + +# 1. 共享信箱 MySQL(dev compose: mysql 服务) +MSGX_MAILBOX_URL=jdbc:mysql://localhost:3306/cdairport?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4 +MSGX_MAILBOX_USER=msgx_dev +MSGX_MAILBOX_PASSWORD=msgx_dev_pass + +# 2. 自有 PostgreSQL(dev compose: postgres 服务,Flyway V1.0.0 自动建表) +MSGX_PG_URL=jdbc:postgresql://localhost:5432/msgx +MSGX_PG_USER=msgx_dev +MSGX_PG_PASSWORD=msgx_dev_pass + +# 3. Redis / Valkey(dev compose: redis 服务,航班动态 + 快照 gen) +MSGX_REDIS_URI=redis://localhost:6379 + +# 4. Kafka(dev compose: kafka 单节点 KRaft 服务,或对接现网/测试网) +MSGX_KAFKA_SERVERS=localhost:9092 + +# --- 旧版 Kafka Broker 兼容配置(如现网 0.10.x / 1.x 无 INIT_PRODUCER_ID 协议时启用)--- +# 现代 Kafka(3.x/4.x,如本地 compose):默认 acks=all, idempotence=true, max-in-flight=5 +# 旧版 Kafka(0.10.x/1.x):取消注释以下三行以触发兼容降级,防止 UnsupportedVersionException +# MSGX_KAFKA_ACKS=1 +# MSGX_KAFKA_IDEMPOTENCE=false +# MSGX_KAFKA_MAX_IN_FLIGHT=1 diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..2daeb8f --- /dev/null +++ b/compose.yaml @@ -0,0 +1,104 @@ +# ===================================================================== +# msgexchange-v2 本地开发依赖中间件栈(ACM2-13 口径) +# 兼容 Podman Compose 与 Docker Compose +# 包含: +# - mysql: 共享信箱 MySQL 8.4 LTS(库 cdairport,初始化 CMINMSGS/COUTMSGS) +# - postgres: 自有 PostgreSQL 17-alpine(库 msgx,由应用 Flyway 建表) +# - redis: 航班动态 + 快照 gen 开源替代品 Valkey 8-alpine(兼容 Redis 7.2+ 协议与 Lua) +# - kafka: 单节点 Apache Kafka 3.8.0 KRaft +# ===================================================================== + +services: + mysql: + image: mysql:8.4 + container_name: msgx-dev-mysql + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: root_dev_pass + MYSQL_DATABASE: cdairport + MYSQL_USER: ${MSGX_MAILBOX_USER:-msgx_dev} + MYSQL_PASSWORD: ${MSGX_MAILBOX_PASSWORD:-msgx_dev_pass} + ports: + - "3306:3306" + volumes: + - ./deploy/dev/mysql-init:/docker-entrypoint-initdb.d:ro + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u$$MYSQL_USER -p$$MYSQL_PASSWORD --silent || exit 1"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 15s + + postgres: + image: postgres:17-alpine + container_name: msgx-dev-postgres + restart: unless-stopped + environment: + POSTGRES_DB: msgx + POSTGRES_USER: ${MSGX_PG_USER:-msgx_dev} + POSTGRES_PASSWORD: ${MSGX_PG_PASSWORD:-msgx_dev_pass} + ports: + - "5432:5432" + volumes: + - pg_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB -h 127.0.0.1 || exit 1"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 5s + + redis: + image: valkey/valkey:8-alpine + container_name: msgx-dev-redis + restart: unless-stopped + command: ["valkey-server", "--appendonly", "yes"] + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD-SHELL", "valkey-cli ping | grep PONG || exit 1"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 5s + + kafka: + image: apache/kafka:3.8.0 + container_name: msgx-dev-kafka + restart: unless-stopped + ports: + - "9092:9092" + environment: + # KRaft 单节点角色与集群 ID + KAFKA_NODE_ID: 1 + KAFKA_PROCESS_ROLES: broker,controller + KAFKA_CLUSTER_ID: "4L622nShTUiT8tCdRvgqgA" + # 监听器与协议映射 + KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT + KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER + KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9093 + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + # 单节点副本与分区配置(满足 acks=all + 幂等生产者) + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_DEFAULT_REPLICATION_FACTOR: 1 + KAFKA_NUM_PARTITIONS: 1 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" + KAFKA_LOG_DIRS: /tmp/kraft-combined-logs + healthcheck: + test: ["CMD-SHELL", "/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list || exit 1"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 10s + +volumes: + mysql_data: + pg_data: + redis_data: diff --git a/deploy/dev/mysql-init/01-mailbox.sql b/deploy/dev/mysql-init/01-mailbox.sql new file mode 100644 index 0000000..e7e5f1b --- /dev/null +++ b/deploy/dev/mysql-init/01-mailbox.sql @@ -0,0 +1,66 @@ +-- ===================================================================== +-- deploy/dev/mysql-init/01-mailbox.sql +-- 本地开发环境共享信箱 MySQL 初始化脚本(ACM2-13 / ACM2-12 口径) +-- +-- 数据库:cdairport(他人系统库,本仓库生产不建表/不声明权威 schema; +-- 此处仅供本地 dev compose 容器初始化 CMINMSGS 与 COUTMSGS 供联调与轮询测试)。 +-- 列定义与现役 legacy 实体(Cminmsg.java / Cmoutmsg.java)对齐。 +-- ===================================================================== + +CREATE DATABASE IF NOT EXISTS `cdairport` + DEFAULT CHARACTER SET utf8mb4 + DEFAULT COLLATE utf8mb4_unicode_ci; + +USE `cdairport`; + +-- 入站信箱表(上游写报文原文,本系统 InboxPoller 1s 轮询读取并回填) +CREATE TABLE IF NOT EXISTS `CMINMSGS` ( + `CMINMSGS_ID` BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增消息序号', + `CMINMSGS_CLOB_MSG` LONGTEXT NOT NULL COMMENT '原始 XML 报文内容', + `CMINMSGS_DATE_RECEIVED` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '信箱入库时间', + `CMINMSGS_DATE_PROCESSED` DATETIME DEFAULT NULL COMMENT '处理完成时间(NULL 表示未处理)', + `CMINMSGS_STATUS` VARCHAR(32) DEFAULT NULL COMMENT '处理状态(如 PROCESSED/ERROR)', + `CMINMSGS_SUBSYSTEM_TYPE` VARCHAR(32) DEFAULT NULL COMMENT '子系统类型', + `CMINMSGS_SUBSYSTEM_SUBTYPE` VARCHAR(32) DEFAULT NULL COMMENT '子系统子类型', + `CMINMSGS_SUBSYSTEM_NAME` VARCHAR(64) DEFAULT NULL COMMENT '子系统名称', + `CMINMSGS_SUBSYSTEM_SEQUENCE` BIGINT DEFAULT NULL COMMENT '子系统序列号', + `CMINMSGS_SUBSYSTEM_DATE_SENT` DATETIME DEFAULT NULL COMMENT '子系统发送时间', + PRIMARY KEY (`CMINMSGS_ID`), + INDEX `idx_cminmsgs_poll` (`CMINMSGS_ID`, `CMINMSGS_DATE_PROCESSED`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='[DEV ONLY] 共享入站信箱表'; + +-- 历史归档表(ARCHIVE 任务将 1 天前的已处理信件归档至此) +CREATE TABLE IF NOT EXISTS `CMINMSGS_HST` ( + `CMINMSGS_ID` BIGINT NOT NULL, + `CMINMSGS_CLOB_MSG` LONGTEXT NOT NULL, + `CMINMSGS_DATE_RECEIVED` DATETIME NOT NULL, + `CMINMSGS_DATE_PROCESSED` DATETIME DEFAULT NULL, + `CMINMSGS_STATUS` VARCHAR(32) DEFAULT NULL, + `CMINMSGS_SUBSYSTEM_TYPE` VARCHAR(32) DEFAULT NULL, + `CMINMSGS_SUBSYSTEM_SUBTYPE` VARCHAR(32) DEFAULT NULL, + `CMINMSGS_SUBSYSTEM_NAME` VARCHAR(64) DEFAULT NULL, + `CMINMSGS_SUBSYSTEM_SEQUENCE` BIGINT DEFAULT NULL, + `CMINMSGS_SUBSYSTEM_DATE_SENT` DATETIME DEFAULT NULL, + PRIMARY KEY (`CMINMSGS_ID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='[DEV ONLY] 共享入站信箱历史归档表'; + +-- 出站信箱表(本系统请求类或通知类报文写入,供他人读取发送) +CREATE TABLE IF NOT EXISTS `COUTMSGS` ( + `COUTMSGS_ID` BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增出站序号', + `COUTMSGS_CLOB_MSG` LONGTEXT NOT NULL COMMENT '原始 XML 出站报文', + `COUTMSGS_DATE_INSERTED` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间', + `COUTMSGS_DATE_SENT` DATETIME DEFAULT NULL COMMENT '发送时间', + `ROUTINGID` VARCHAR(64) DEFAULT NULL COMMENT '路由标识(如 OSH5RQFD)', + `COUTMSGS_ACK_REQD` VARCHAR(8) DEFAULT NULL COMMENT '是否需要 ACK', + `COUTMSGS_ACK_DATE_RECV` DATETIME DEFAULT NULL COMMENT '收到 ACK 时间', + `COUTMSGS_ACK_RESEND_TIMES` INT DEFAULT 0 COMMENT '重发次数', + `COUTMSGS_ERROR` VARCHAR(255) DEFAULT NULL COMMENT '错误信息', + `COUTMSGS_GROUP_ID` VARCHAR(64) DEFAULT NULL COMMENT '分组 ID', + `COUTMSGS_GROUP_ORDER` INT DEFAULT NULL COMMENT '组内顺序', + `COUTMSGS_NO_MESSAGES` INT DEFAULT NULL COMMENT '组内消息数', + `COUTMSGS_FINAL_GROUP_IND` VARCHAR(8) DEFAULT NULL COMMENT '最终组标记', + `COUTMSGS_TRUEFALS_GROUP` VARCHAR(8) DEFAULT NULL COMMENT '分组布尔标记', + `COUTMSGS_ENCRYPT` VARCHAR(8) DEFAULT NULL COMMENT '加密标记', + PRIMARY KEY (`COUTMSGS_ID`), + INDEX `idx_coutmsgs_sent` (`COUTMSGS_DATE_SENT`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='[DEV ONLY] 共享出站信箱表'; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 475d1e8..de639de 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -18,17 +18,24 @@ eureka: enabled: false # DB 仅在真实实装接入后需要;stub 模式不建连——datasources.default.enabled=false 经 -# micronaut-jdbc 的 JdbcDataSourceEnabled 条件排除 DataSource bean("stub 不建连"落地); -# 下方 url 仍保留合法默认(host/port/name 分拆,避免占位符默认值内嵌冒号解析坏)。 -# 真实接入时:设 MSGX_DB_URL(或 MSGX_DB_HOST/PORT/NAME)+ enabled=true。 +# micronaut-jdbc 的 JdbcDataSourceEnabled 条件排除 DataSource bean("stub 不建连"落地)。 +# 遵循 ACM2-12/ACM2-13 自有数据库(PostgreSQL)口径。真实接入时:设 MSGX_PG_URL + enabled=true。 datasources: default: enabled: false - url: jdbc:mysql://${MSGX_DB_HOST:127.0.0.1}:${MSGX_DB_PORT:3306}/${MSGX_DB_NAME:msgexchange_dev} - username: ${MSGX_DB_USER:root} - password: ${MSGX_DB_PASSWORD:} - driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:postgresql://${MSGX_PG_HOST:127.0.0.1}:${MSGX_PG_PORT:5432}/${MSGX_PG_NAME:msgx} + username: ${MSGX_PG_USER:msgx_dev} + password: ${MSGX_PG_PASSWORD:msgx_dev_pass} + driver-class-name: org.postgresql.Driver +# 共享信箱 MySQL 适配配置(ACM2-12/ACM2-13): +mailbox: + shared-mysql: + enabled: false + url: jdbc:mysql://${MSGX_MAILBOX_HOST:127.0.0.1}:${MSGX_MAILBOX_PORT:3306}/${MSGX_MAILBOX_NAME:cdairport}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4 + username: ${MSGX_MAILBOX_USER:msgx_dev} + password: ${MSGX_MAILBOX_PASSWORD:msgx_dev_pass} + driver-class-name: com.mysql.cj.jdbc.Driver # stub 模式无 DataSource:Flyway 显式关闭(主配置默认 enabled:true 只作用于有 DataSource 的环境) flyway: datasources: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 425db47..60c5c6a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -58,6 +58,7 @@ flyway: # (CminmsgMailbox/OutboxMailbox)随 U05 批次引入。 mailbox: shared-mysql: + enabled: false url: ${MSGX_MAILBOX_URL} username: ${MSGX_MAILBOX_USER} password: ${MSGX_MAILBOX_PASSWORD} @@ -71,9 +72,11 @@ kafka: servers: ${MSGX_KAFKA_SERVERS} producers: default: # U03/R09:Micronaut Kafka 按具名 producer 解析,须有 default 层 - acks: all - enable-idempotence: true - + # 默认 acks=all + 幂等(现代 Kafka 3.x+);若对接旧版 Broker(如现网 0.10.x/1.x,无 INIT_PRODUCER_ID 协议), + # 经 MSGX_KAFKA_ACKS=1 与 MSGX_KAFKA_IDEMPOTENCE=false 降级兼容。 + acks: ${MSGX_KAFKA_ACKS:all} + enable-idempotence: ${MSGX_KAFKA_IDEMPOTENCE:true} + max-in-flight-requests-per-connection: ${MSGX_KAFKA_MAX_IN_FLIGHT:5} eureka: client: registration: