# FIX Engine 高可用 (HA) 架构方案说明文档 ## 1. 架构演进概述 ### 1.1 原单实例架构 - **消息存储**: 使用本地文件系统(FileStore),消息持久化在本地磁盘。 - **灾备能力**: 无自动切换机制。若实例宕机,需手动启动备机,且由于文件存储不共享,消息序列号(Sequence Number)难以同步。 - **会话管理**: 启动即连接,缺乏灵活性。 ### 1.2 现高可用 (HA) 架构 - **消息存储**: 迁移至 **MySQL 数据库**(JdbcStore)。多实例共享同一数据库,确保消息和序列号在节点切换后保持一致。 - **领导权选举**: 引入 **Redis 分布式锁**(Redisson)。以 Session 为粒度进行选举,确保每个 FIX 会话在全网只有一个活动实例。 - **自动灾备**: 备机定时检查锁状态,主节点宕机后锁释放,备机自动接管并恢复连接。 - **动态会话**: 结合 DynamicSession 机制,仅在获得领导权后才创建并启动 FIX 连接。 --- ## 2. 核心组件分析 ### 2.1 MySQL 消息持久化 (JdbcStore) 通过 `QuickFixJConfig` 配置,系统支持将消息存储从文件切换到 MySQL。 - **核心类**: `quickfix.JdbcStoreFactory` - **配置触发**: 当 `StorageType=mysql` 时启用。 - **数据表要求**: 需要在数据库中创建 QuickFIX/J 标准表(如 `messages`, `sessions` 等)。 - **优势**: - **共享状态**: 所有实例访问同一份数据,切换节点无需手动同步序列号。 - **可靠性**: 数据库事务保障消息持久化。 ### 2.2 Redis 分布式锁选举 (LeaderElectionService) 使用 Redisson 实现 Session 级别的分布式锁管理。 - **锁 Key 格式**: `g3fo:fix-engine:session-lock:{SenderCompID}@{TargetCompID}` - **看门狗机制**: 租约时间设为 -1,Redisson 自动续期。只要进程存活,锁就不会过期。 - **检查机制**: 定时线程(默认 10 秒)遍历所有配置的 Session。 - **宕机延迟 (Failover Delay)**: 检测到锁释放后,等待 5 秒再尝试抢占,防止因网络抖动引起的频繁切换。 ### 2.3 动态会话控制 (Dynamic Session) 解决 QuickFIX/J 在 `start()` 时会自动连接所有会话的问题。 - **机制**: 在配置文件中将 Session 标记为 Dynamic(不预先加载)。 - **流程**: 1. `LeaderElectionService` 获得 Redis 锁。 2. 调用 `quickFixJConfig.startSession(sessionCode)`。 3. 内部通过 `socketInitiator.createDynamicSession(sessionId)` 动态创建会话对象。 4. 调用 `session.logon()` 触发连接。 --- ## 3. 完整业务流程 ### 3.1 服务启动流程 ```mermaid flowchart TD Start([服务启动]) --> InitInitiator[初始化 QuickFIX Initiator] InitInitiator --> StartElection[启动 LeaderElectionService] StartElection --> LoadSessions[加载 enabledSession 配置] LoadSessions --> LoopCheck{遍历每个 Session} LoopCheck --> TryLock[尝试获取 Redis 分布式锁] TryLock -- 成功获得锁 --> StartComp[启动会话组件] StartComp --> CreateDynamic[创建 DynamicSession 对象] CreateDynamic --> FixLogon[发送 FIX Logon] FixLogon --> StartMQ[启动对应的 RocketMQ Listener] TryLock -- 失败 --> Standby[进入待命状态] Standby --> Wait[等待下一个检查周期] Wait --> LoopCheck ``` ### 3.2 宕机自动切换流程 (Failover) 1. **主节点 (Node A)** 持有 `A@OCG` 的 Redis 锁,正常运行。 2. **主节点 (Node A)** 宕机或网络断开。 3. **Redis 锁失效**: 经过看门狗租约超时,Redis 中的锁 Key 消失。 4. **备节点 (Node B)** 定时任务检测到锁已释放。 5. **进入延迟等待**: 备节点等待 `failoverDelay` (如 5秒)。 6. **抢占锁**: 备节点尝试 `tryLock`,成功获得领导权。 7. **恢复连接**: 备节点动态创建 Session 并在 MySQL 中读取最新的序列号,发送 Logon。 8. **流量接管**: 启动 MQ Listener,开始处理业务消息。 --- ## 4. 关键配置指南 (Nacos) 在 `quick-fix-client.yml` 中进行如下配置: ```yaml quickfixj: client: # 启用会话列表 enabledSession: A@OCG,A@OSL # 选举参数 sessionElection: checkInterval: 10 # 检查周期(秒) failoverDelay: 5 # 接管延迟(秒) # 存储配置(需配合数据源) # 确保 SessionSettings 中的 StorageType=mysql ``` --- ## 5. 日志监控与诊断 通过监控日志可以实时掌握 HA 状态: | 日志内容 | 含义 | 级别 | | :--- | :--- | :--- | | `>>> 获得Session A@OCG 领导权 <<<` | 当前实例成功竞争到主节点 | INFO | | `!!! Session A@OCG 领导权锁意外丢失 !!!` | 锁异常丢失,可能是 Redis 连接断开 | ERROR | | `正在启动Session A@OCG 组件...` | 获得锁后开始加载 FIX 和 MQ | INFO | | `✅ Session A@OCG 组件启动完成` | 成功恢复服务 | INFO | | `未获得领导权,作为备用实例运行中...` | 当前为备份节点,正常待命 | DEBUG | --- ## 6. 总结 本方案通过 **MySQL 共享存储 + Redis 分布式选举 + 动态会话加载** 的组合,解决了 FIX 引擎单点故障问题。它不仅保证了消息的连续性(序列号一致),还实现了会话级别的细粒度高可用,能够灵活应对多种部署场景。