提交 2fe45fe4 authored 作者: 陈泽健's avatar 陈泽健

docs(双机热备): 新增新统一平台高可用架构设计与架构图

- 5节点压缩版(当前项目): 3台通用机对称全栈混部 + 2台达梦专用机, Redis主从+哨兵, dmmonitor置备机DM-B
- 8节点全分层版(备选/后续项目): 应用接入2 + 中间件3 + 数据库3
- 含可视化SVG架构图(HTML,支持导出PDF/SVG)与分析文档
- 架构按X86+麒麟V10, 含故障切换/RTO RPO/风险评估/实施阶段
- 8节点版保留供资源充裕项目复用
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 971ed0ee
# 新统一平台 高可用架构设计分析(5 节点压缩版 · 当前项目)
> 📌 **当前项目采用本稿**(受甲方硬件约束)。
> 资源充裕的备选方案见 [`_双机热备架构设计分析_8节点全分层版.md`](./_双机热备架构设计分析_8节点全分层版.md)(8 节点三层,后续项目可复用)。
> 适用环境:X86 架构 + 麒麟 V10(Kylin V10 Server,X86 版)
> 中间件选型:Tengine 3.1.0、FastDFS、EMQX、Nacos、达梦 DM8、Redis(主从+哨兵,本团队部署)
> 规模:**5 节点**(3 台通用机全栈混部 + 2 台达梦专用机)
> 文档类型:架构设计分析(设计阶段输出,非施工文档)
---
## 1. 背景与目标
### 1.1 背景与硬件约束
甲方硬件供给:
- **3 台通用服务器** —— 承载应用接入 + 全部中间件(Nacos/EMQX/FastDFS/Redis)+ 达梦监视器;
- **2 台达梦专用服务器** —— **仅能搭建达梦**,不承载其它服务。
其余约束:国产化信创栈、X86 + 麒麟 V10(X86 版)、Redis 由本团队部署集群。
### 1.2 设计目标
| 目标 | 说明 |
| --- | --- |
| 高可用 | 任一节点宕机,业务秒~分钟级自动恢复,数据零/近零丢失 |
| 适配 5 机约束 | 应用与中间件全栈混部于 3 台通用机,达梦独立 2 台 |
| 单机故障可容忍 | 任一通用机宕,各集群仍满足多数派/副本 |
| 国产合规 | 全栈国产/自主可控 |
| 运维友好 | 故障可观测、可回切、可演练 |
---
## 2. 总体设计思路
受"仅 3 通用 + 2 达梦"约束,无法做应用/中间件独立分层,采用 **"3 通用节点全栈混部 + 2 达梦专用主备"**
| 层 | 节点 | HA 模式 |
| --- | --- | --- |
| ① 通用节点(应用+中间件) | N1 / N2 / N3 | **3 节点集群 + Keepalived VIP**(应用主备、中间件集群、Redis 哨兵、达梦监视器) |
| ② 达梦数据库(专用) | DM-A(主)/ DM-B(备) | **DMDataWatch 主备**;确认监视器 dmmonitor 部署在备机 DM-B |
> 关键:① 3 台通用机对称全栈、互为 HA,VIP 浮动;② 应用主备(VIP),中间件 3 节点集群;③ 达梦 2 台专用机仅跑达梦,dmwatcher 随达梦部署,dmmonitor 置备机 DM-B。
---
## 3. 总体架构图
> 🖼️ **可视化架构图**:见同目录 [`双机热备架构图.html`](./双机热备架构图.html)(5 节点当前版)|备选 [`双机热备架构图_8节点全分层版.html`](./双机热备架构图_8节点全分层版.html)。
### 3.1 节点清单
| 节点 | IP | 角色 | 部署内容 |
| --- | --- | --- | --- |
| N1 | 192.168.9.10 | 通用(VIP 主) | Tengine+前端+后端 · Nacos#1 · EMQX#1 · FastDFS Tracker#1+Storage#1 · Redis**主**+哨兵#1 |
| N2 | 192.168.9.11 | 通用(VIP 备) | Tengine+前端+后端 · Nacos#2 · EMQX#2 · FastDFS Tracker#2+Storage#2 · Redis**从1**+哨兵#2 |
| N3 | 192.168.9.12 | 通用(VIP 备) | Tengine+前端+后端 · Nacos#3 · EMQX#3 · FastDFS Tracker#3+Storage#3 · Redis**从2**+哨兵#3 |
| DM-A | 192.168.9.30 | 达梦(主库) | 达梦 DM8 主库 + dmwatcher |
| DM-B | 192.168.9.31 | 达梦(备库+监视器) | 达梦 DM8 备库 + dmwatcher + **dmmonitor 确认监视器** |
| VIP | 192.168.9.100 | — | 对外入口(N1/N2/N3 间浮动) |
### 3.2 逻辑拓扑
```mermaid
flowchart TB
U["用户/终端/IoT"] --> VIP["<b>VIP 192.168.9.100</b><br/>Keepalived VRRP"]
subgraph L1["① 通用节点 ×3(全栈混部)"]
direction LR
N1["N1 .10(VIP主)<br/>Tengine+前端+后端·Nacos#1·EMQX#1<br/>FDFS Tracker#1+Storage#1·Redis主+哨兵1"]
N2["N2 .11(VIP备)<br/>Tengine+前端+后端·Nacos#2·EMQX#2<br/>FDFS Tracker#2+Storage#2·Redis从1+哨兵2"]
N3["N3 .12(VIP备)<br/>Tengine+前端+后端·Nacos#3·EMQX#3<br/>FDFS Tracker#3+Storage#3·Redis从2+哨兵3"]
N1 <-->|"集群互连"| N2
N2 <-->|"集群互连"| N3
end
VIP --> N1
VIP -.-> N2
VIP -.-> N3
subgraph L2["② 达梦专用 ×2"]
direction LR
DMA["DM-A .30 达梦主库 PRIMARY<br/>dmwatcher"]
DMB["DM-B .31 达梦备库 STANDBY<br/>dmwatcher + dmmonitor"]
DMA -.->|"Redo 实时同步"| DMB
end
DMB -.->|"dmmonitor 监控/仲裁"| DMA
N1 -->|"读写主库(JDBC ep_list)"| DMA
N2 -->|"读写主库"| DMA
N3 -->|"读写主库"| DMA
```
---
## 4. 网络与端口规划
| 网络 | 用途 |
| --- | --- |
| 业务网 eth0 | 对外服务、VIP、节点间通信 |
| 心跳网 eth1(建议独立) | Keepalived VRRP、达梦 MAL Redo 同步 |
端口:Tengine 80/443 | Nacos 8848/9848/7848 | EMQX 1883/8083/18083 | FastDFS 22122/23000 | 达梦 5236/5238/5239 | Redis 6379 / 哨兵 26379。
---
## 5. 组件级高可用设计
### 5.1 应用接入(Tengine + 前端 + 后端,主备 + VIP)
| 项 | 设计 |
| --- | --- |
| 部署 | N1/N2/N3 各跑 Tengine 3.1.0 + 前端 + 后端,**同机** |
| VIP | Keepalived:N1=MASTER(priority 最高)、N2/N3=BACKUP,VIP 浮动 |
| 模式 | 主备(同一时刻仅 VIP 所在节点对外);备机整台热备 |
| 切换时间 | 约 1~3 秒 |
> ⚠️ 备机后端连**主库**(JDBC ep_list),不读延迟数据。
### 5.2 中间件集群(N1/N2/N3)
| 组件 | 设计 |
| --- | --- |
| **Nacos** | 3 节点(一机一个),Raft 多数派,容忍 1 节点宕机;配置外置达梦 |
| **EMQX** | 3 节点集群,Mnesia 同步,`$share/` 共享订阅 |
| **FastDFS** | 3 Tracker(每机一个)+ 3 Storage(N1/N2/N3 同组三副本互保) |
| **Redis** | **1 主(N1) + 2 从(N2/N3) + 3 哨兵**(每机一个),quorum=2;主宕→哨兵选从升主 |
### 5.3 达梦数据库(DM-A / DM-B,专用机)
| 项 | 设计 |
| --- | --- |
| 模式 | DMDataWatch 主备,实时同步(REALTIME SYNC) |
| 角色 | DM-A=主库;DM-B=备库;dmwatcher 随达梦部署于两机 |
| 监视器 | **dmmonitor 确认监视器部署在备机 DM-B**(达梦组件;主机宕时备机存活可正常仲裁) |
| 一致性 | RPO≈0;切换 10~60s(RTO 瓶颈) |
> ⚠️ dmmonitor 在 DM-B(备机):覆盖"主库 DM-A 故障"的自动切换场景(备机存活→仲裁→升主);缺点是非独立第三方,网络脑裂时仲裁弱于独立节点(靠独立心跳网卡缓解)。若更看重抗脑裂,可把 dmmonitor 改放通用节点 N1/N2。
### 5.4 应用访问数据库(★JDBC 主备 ep_list)
达梦主备切换后 DM-B 升主。应用用 **达梦 JDBC 主备连接串(ep_list 同时配 DM-A、DM-B)**,驱动自动连当前主库,切换后零改配置自动重连。
---
## 6. 数据同步与一致性
| 组件 | 同步方式 | RPO | 一致性 |
| --- | --- | --- | --- |
| 达梦 DM8 | Redo 实时同步(MAL) | ≈0 | 强一致 |
| Redis | 主从异步 + 哨兵 | 极小 | 最终一致 |
| FastDFS | Storage 同组互保 | 极小 | 最终一致 |
| EMQX | Mnesia 集群同步 | 极小 | 最终一致 |
| Nacos | Distro/Raft 集群同步 | 极小 | 配置强/注册最终 |
---
## 7. 故障切换
| 故障 | 处理 | RTO | RPO |
| --- | --- | --- | --- |
| N1(VIP主/Redis主)宕 | VIP→N2/N3;哨兵(剩2)选从升主;FastDFS 走 N2 副本;Nacos/EMQX 剩 2/3 仍可用 | 秒级 | 极小 |
| N2 或 N3 宕 | 集群自愈;VIP/Redis 主若存活则不影响;dmmonitor 在 DM-B,通用机宕不影响达梦切换 | 秒级 | 极小 |
| Redis 主宕 | 哨兵 quorum=2 选从升主 | 秒级 | 极小 |
| DM-A 主库宕 | dmwatcher+dmmonitor(DM-B) 仲裁→DM-B 升主;JDBC ep_list 自动连新主 | 10~60s | ≈0 |
| 计划内回切 | dmmonitor 手动 Switchover;Keepalived 视策略抢占 | — | — |
> ⚠️ **不可容忍**:2 台通用机同时故障 → 集群失多数派、Redis/Storage 副本不足,系统不可用(3 节点集群固有上限)。
---
## 8. 资源与目录规划
| 节点 | 关键目录 |
| --- | --- |
| N1/N2/N3 | `/data/tengine``/data/services/api`(前后端)、`/data/nacos``/data/emqx``/data/fastdfs`(Tracker+Storage)、`/data/redis`(含哨兵) |
| DM-A/B | `/data/dmdbms``/data/dmdata`;DM-B 另 `/data/dmmonitor` |
> 通用节点负载重,建议 **16C 32G + SSD** 起步,重点监控 Redis 内存 / FastDFS 磁盘 / Nacos JVM 水位。
---
## 9. 监控与运维
复用 `AuxiliaryTool/ScriptTool/ServiceSelfInspection``XTY-MONITOR`,扩展检查:**VIP 归属 / 达梦主备角色 / Redis 哨兵主从 / Nacos·EMQX 节点数 / FastDFS Storage 状态 / 通用节点资源水位**
---
## 10. 风险评估(国产化 / X86 / 麒麟 V10 / 5 机约束)
| # | 风险 | 影响 | 应对 |
| --- | --- | --- | --- |
| 1 | 通用机负载重(全栈混部) | 资源争抢、单点过载 | 16C32G+、监控水位、必要时升 8 节点全分层方案 |
| 2 | dmmonitor 在 DM-B(非独立第三方) | 网络脑裂时独立仲裁弱于独立节点 | 靠独立心跳网卡缓解;或改放通用节点 N1/N2 |
| 3 | 3 节点集群不可抗 2 节点故障 | 系统不可用 | 接受(HA 上限);加强单机可靠性/巡检 |
| 4 | 组件包兼容性 | Tengine/FastDFS/Redis 需匹配麒麟 V10 X86 | 优先官方 rpm/源码包,验证 glibc 兼容 |
| 5 | 达梦切换后连旧主库 | 写入失败 | JDBC 主备 ep_list(§5.4) |
| 6 | Redis 单主无分片 | 写入集中 | 会议系统够用;重负载升 Cluster |
| 7 | 脑裂 | 双主数据冲突 | 独立心跳网卡 + dmmonitor 仲裁 |
| 8 | 时钟漂移 | 集群异常 | 全节点 NTP/Chrony |
| 9 | 授权 | 多节点 X86 达梦/meeting | 各节点申请/激活 |
---
## 11. 实施阶段建议
- [ ] 阶段 0:5 节点装机、网络(双网卡)、时钟、依赖源(Tengine/FastDFS/Redis X86 包)
- [ ] 阶段 1:达梦主备(DM-A/DM-B)+ DM-B dmmonitor,验证切换 + JDBC ep_list
- [ ] 阶段 2:Redis 主从+3 哨兵(N1/N2/N3),验证 failover
- [ ] 阶段 3:Nacos 3 / EMQX 3 / FastDFS(3 Tracker + 3 Storage 同组三副本)
- [ ] 阶段 4:N1/N2/N3 部署 Tengine+前后端,Keepalived VIP
- [ ] 阶段 5:逐节点故障演练(关机/拔网线),记录 RTO/RPO
- [ ] 阶段 6:监控接入、运维手册
---
## 12. 待确认事项
1. 3 通用机规格(建议 16C32G+)?是否物理机?
2. 独立心跳网卡?
3. Redis 持久化(AOF/RDB)与内存上限?
4. 达梦接入确认用 JDBC ep_list?
5. Tengine 锁定 3.1.0(X86)?
6. Keepalived 是否抢占?
7. 各节点授权申请分发?
8. Redis 运维边界与甲方书面明确?
---
## 13. 结论
本方案在 **"3 通用 + 2 达梦"** 硬件约束下:
- **3 通用节点对称全栈**,互为 HA:应用主备+VIP、Nacos/EMQX 3 节点集群、FastDFS 三副本、Redis 主从+哨兵;
- **2 达梦专用机**主备实时同步(RPO≈0),仅跑达梦;
- 应用↔DB 用 JDBC 主备 ep_list 自动容错;
- **RTO 接入/缓存秒级、数据库 10~60s,RPO≈0**
- 单通用机故障可容忍,满足多数派/副本要求。
主要取舍:通用机密度高(需强配置+监控)、dmmonitor 在备机 DM-B(非独立第三方)、3 节点不可抗 2 故障。资源充裕时可切换 8 节点全分层方案。
---
### 文档变更记录
| 版本 | 日期 | 变更 | 作者 |
| --- | --- | --- | --- |
| v0.1~v0.4 | 2026-07-03 | 两机→8 节点全分层演进(见 _8节点全分层版.md) | — |
| v0.5 | 2026-07-03 | 按甲方约束改为 5 节点(3 通用全栈混部 + 2 达梦专用),dmmonitor 置 N3;8 节点版另存为备选 | — |
| v0.6 | 2026-07-03 | **当前项目定稿**:N3 去掉 dmmonitor、改对称加 Storage#3(FastDFS 三副本);dmmonitor 移至备机 DM-B | — |
# 新统一平台 高可用架构设计分析(8 节点全分层版 · 备选)
> 📌 **说明**:本稿为 **8 节点全分层** 方案(应用接入 2 + 中间件 3 + 数据库 3),资源充裕时采用,**留作后续项目备选**。
> 当前项目受甲方约束(仅 3 台通用机 + 2 台达梦专用机),实际采用 **5 节点压缩版**,见 [`_双机热备架构设计分析.md`](./_双机热备架构设计分析.md)。
> 适用环境:ARM 架构 + 麒麟 V10(Kylin V10 Server)
> 中间件选型:Tengine 3.1.0、FastDFS、EMQX、Nacos、达梦 DM8、Redis(主从+哨兵,本团队部署)
> 规模:**8 节点 · 三层冗余**(应用接入 2 + 中间件 3 + 数据库 3)
> 文档类型:架构设计分析(设计阶段输出,非施工文档)
---
## 1. 背景与目标
### 1.1 背景
新统一平台需在生产环境提供**高可用(HA)**能力:
- **国产化要求**:中间件、数据库须采用国产/信创栈;
- **硬件架构**:ARM(飞腾/鲲鹏等)CPU,操作系统为麒麟 V10 Server;
- **"双机热备"= 分层冗余 HA**:并非限定"只有两台服务器",而是**每层关键组件都有冗余**,用多台服务器合理组网;
- **Redis 由本团队部署**:虽属甲方资产,但集群由我们搭建与维护。
### 1.2 设计目标
| 目标 | 说明 |
| --- | --- |
| 高可用 | 任一节点宕机,业务秒~分钟级自动恢复,**数据零/近零丢失** |
| 三层分离 | 应用接入 / 中间件 / 数据库各自独立、各自冗余,故障爆炸半径小 |
| 国产合规 | 全栈中间件/数据库均为国产或自主可控开源 |
| 适度混部 | 同层内轻量组件混部以省机器,层间职责清晰 |
| 运维友好 | 故障切换可观测、可回切、可演练 |
---
## 2. 总体设计思路
采用 **三层冗余 + 主备/集群**
| 层 | 节点 | HA 模式 |
| --- | --- | --- |
| ① 应用接入层 | APP-1(主)/ APP-2(备) | **主备 + Keepalived VIP**(Tengine+前端+后端同机,VIP 跟随) |
| ② 中间件层 | MW-1 / MW-2 / MW-3 | **3 节点集群**(Nacos / EMQX / FastDFS / Redis 主从+哨兵) |
| ③ 数据库层 | DM-A(主)/ DM-B(备)/ DM-MON(监视器) | **达梦 DMDataWatch 主备 + 独立确认监视器** |
> 关键决策:① 应用层**主备**(非双活)——简单、无会话分发问题,备机整台热备;② 中间件 **3 节点**同时满足 Nacos Raft 多数派与 Redis 哨兵 quorum=2;③ 达梦**独立 3 台**,IO 隔离 + 独立仲裁最稳。
---
## 3. 总体架构图
> 🖼️ **可视化架构图(推荐)**:见同目录 [`双机热备架构图_8节点全分层版.html`](./双机热备架构图_8节点全分层版.html) —— 浏览器直接打开,支持「打印/导出 PDF」「导出 SVG」,含 8 节点三层彩色拓扑与图例。
### 3.1 逻辑拓扑图
```mermaid
flowchart TB
User["用户 / 终端 / IoT 设备"]
VIP["<b>VIP 192.168.9.100</b><br/>Keepalived VRRP"]
User --> VIP
subgraph L1["① 应用接入层(主备 · VIP 跟随)"]
direction LR
APP1["APP-1 .10(主)<br/>Tengine + 前端 + 后端<br/>Keepalived MASTER"]
APP2["APP-2 .11(备)<br/>Tengine + 前端 + 后端<br/>Keepalived BACKUP"]
APP1 <-->|"VRRP 心跳"| APP2
end
VIP -->|"默认"| APP1
VIP -.->|"故障切换"| APP2
subgraph L2["② 中间件层(3 节点集群)"]
direction LR
MW1["MW-1 .20<br/>Nacos#1·EMQX#1<br/>FDFS Tracker#1+Storage#1<br/>Redis 主 + 哨兵1"]
MW2["MW-2 .21<br/>Nacos#2·EMQX#2<br/>FDFS Tracker#2+Storage#2<br/>Redis 从1 + 哨兵2"]
MW3["MW-3 .22<br/>Nacos#3·EMQX#3<br/>FDFS Tracker#3<br/>Redis 从2 + 哨兵3"]
MW1 <-->|"集群互连"| MW2
MW2 <-->|"集群互连"| MW3
end
subgraph L3["③ 数据库层(达梦 DMDataWatch)"]
direction LR
DMA["DM-A .30<br/>达梦主库 PRIMARY<br/>dmwatcher"]
DMB["DM-B .31<br/>达梦备库 STANDBY<br/>dmwatcher"]
DMM["DM-MON .32(独立)<br/>dmmonitor 确认监视器"]
DMA -.->|"Redo 实时同步"| DMB
DMM -.->|"监控/仲裁"| DMA
DMM -.->|"监控/仲裁"| DMB
end
APP1 -->|"访问 Nacos/Redis/EMQX/FastDFS"| MW2
APP2 -->|"访问 Nacos/Redis/EMQX/FastDFS"| MW2
APP1 -->|"读写主库(JDBC 主备 ep_list)"| DMA
APP2 -.->|"读写主库"| DMA
```
### 3.2 节点清单
| 节点 | IP | 层 | 部署内容 |
| --- | --- | --- | --- |
| APP-1 | 192.168.9.10 | 应用接入(主) | Tengine + 前端 + 后端 + Keepalived(MASTER) |
| APP-2 | 192.168.9.11 | 应用接入(备) | Tengine + 前端 + 后端 + Keepalived(BACKUP) |
| MW-1 | 192.168.9.20 | 中间件 | Nacos#1 · EMQX#1 · FastDFS Tracker#1+Storage#1 · Redis**主**+哨兵#1 |
| MW-2 | 192.168.9.21 | 中间件 | Nacos#2 · EMQX#2 · FastDFS Tracker#2+Storage#2 · Redis**从1**+哨兵#2 |
| MW-3 | 192.168.9.22 | 中间件 | Nacos#3 · EMQX#3 · FastDFS Tracker#3 · Redis**从2**+哨兵#3 |
| DM-A | 192.168.9.30 | 数据库(主) | 达梦 DM8 **主库** + dmwatcher |
| DM-B | 192.168.9.31 | 数据库(备) | 达梦 DM8 **备库** + dmwatcher |
| DM-MON | 192.168.9.32 | 数据库(监视器) | **dmmonitor 确认监视器**(独立) |
| VIP | 192.168.9.100 | — | 对外统一入口(APP-1/APP-2 间浮动) |
> 实际 IP 以现场网段为准;建议业务网同网段,并预留独立心跳网卡用于 VRRP / 达梦 MAL。
---
## 4. 网络与端口规划
### 4.1 网络
| 网络 | 用途 | 说明 |
| --- | --- | --- |
| 业务网(eth0) | 对外服务、VIP、应用↔中间件↔DB 通信 | 全节点同网段 |
| 心跳/同步网(eth1) | Keepalived VRRP、达梦 Redo 同步 | 建议独立网卡,降低脑裂风险 |
### 4.2 端口(摘要)
| 组件 | 端口 |
| --- | --- |
| Tengine | 80 / 443 |
| Nacos | 8848 / 9848 / 7848 |
| EMQX | 1883 / 8083 / 18083 |
| FastDFS Tracker / Storage | 22122 / 23000 |
| 达梦 DM8 / 守护 | 5236 / 5238 / 5239 |
| Redis / 哨兵 | 6379 / 26379 |
---
## 5. 组件级高可用设计
### 5.1 应用接入层:APP-1 / APP-2(主备 + VIP)
| 项 | 设计 |
| --- | --- |
| 部署 | APP-1/APP-2 各装 Tengine 3.1.0 + 前端 + 后端,**同机部署** |
| VIP | Keepalived VRRP,APP-1=MASTER、APP-2=BACKUP,VIP 跟随主备 |
| 模式 | **主备**(主机承接流量,备机热备);非双活,无会话分发问题 |
| 健康检查 | `vrrp_script` 检测 Tengine,失败降权触发切换 |
| 切换模式 | 推荐非抢占(nopreempt),避免恢复后二次抖动 |
| 切换时间 | 约 1~3 秒(VRRP),**备机整台接管** |
> ⚠️ 备机后端建议**热备**(进程常驻、无流量),且通过 **JDBC 主备 ep_list** 连达梦(见 5.4),确保不读延迟数据。
> ⚠️ 讯飞 `python-voice` 在 ARM 下可能未适配,按"预期失败/降级"处理。
> 💡 可选升级:若将来想榨干 APP-2 算力,可改双活(Tengine upstream 同时挂两台后端做负载均衡)。
### 5.2 中间件层:MW-1 / MW-2 / MW-3(3 节点集群)
#### 5.2.1 Nacos(3 节点集群)
| 项 | 设计 |
| --- | --- |
| 部署 | MW-1/2/3 各一节点,3 节点 |
| 配置存储 | 外置到达梦 DM8,配置 HA 跟随数据库 |
| 一致性 | Distro(AP)+Raft(CP),3 节点容忍 1 节点宕机 |
| 客户端 | `mw-1:8848,mw-2:8848,mw-3:8848` |
#### 5.2.2 EMQX(3 节点集群)
| 项 | 设计 |
| --- | --- |
| 部署 | MW-1/2/3 各一节点,静态发现组 3 节点集群 |
| 数据 | Mnesia 集群同步(订阅路由、会话) |
| 共享订阅 | 业务用 `$share/group/topic` 负载均衡互备 |
#### 5.2.3 FastDFS(3 Tracker + 2 Storage)
| 项 | 设计 |
| --- | --- |
| Tracker | MW-1/2/3 各一个(共 3),客户端随机连接 |
| Storage | MW-1(#1)、MW-2(#2) 划入**同组**,组内互为副本 |
| 效果 | 任一 Storage 宕机,另一份完整副本可读;任一 Tracker 宕机不影响 |
#### 5.2.4 Redis(1 主 + 2 从 + 3 哨兵)★本团队部署
| 项 | 设计 |
| --- | --- |
| 形态 | **主从 + 哨兵 Sentinel** |
| 分布 | MW-1=主;MW-2=从1;MW-3=从2;每节点各跑 1 个哨兵(共 3) |
| 故障转移 | 主宕机 → 3 哨兵投票(quorum=2) → 选一个从升主,客户端自动重连 |
| 客户端 | 应用使用哨兵地址连接(`mw-1,mw-2,mw-3:26379`),SDK 自动感知新主 |
| 读写 | 主写、从可读;**单主写入(无分片)** |
> 说明:1 主 2 从 + 3 哨兵是经典 Sentinel HA,运维简单、适合会议系统的写入量。
> ⚠️ 单主 = 无分片、单点写入:若未来缓存数据量很大或写入极重,可平滑升级为 Redis Cluster(分片)。当前阶段 Sentinel 足够。
> ⚠️ 资源:Redis 吃内存、FastDFS Storage 吃磁盘,同在 MW-1/2 上需关注 RAM/磁盘水位。
### 5.3 数据库层:达梦 DM8(DMDataWatch)★核心
| 项 | 设计 |
| --- | --- |
| 模式 | **DMDataWatch 主备**,实时同步(REALTIME SYNC) |
| 角色 | DM-A=主库 Primary;DM-B=备库 Standby |
| 同步链路 | MAL 系统,走心跳网,传输 Redo |
| 守护进程 | 每库一个 **dmwatcher**(达梦自带) |
| 监视器 | **dmmonitor 确认监视器**,部署于独立 **DM-MON** 节点 |
| 切换方式 | 自动故障切换 + 手动 Switchover |
| 数据一致性 | 实时同步 + 完全确认模式,**RPO≈0** |
| 切换时间 | 约 10~60s(含 Redo 应用与角色切换),**RTO 瓶颈** |
**监视器为何独立 DM-MON**:达梦自动切换依赖确认监视器在线;放主库机则主机宕时监视器同亡、无法自动切;独立 DM-MON 在主机宕时仍存活,正常仲裁,且网络脑裂时提供独立裁判。
### 5.4 应用访问数据库(★关键:故障切换接入)
达梦主备切换后,备库 DM-B 升为新主库。应用如何找到新主库:
- **推荐:达梦 JDBC 驱动主备连接串(ep_list)** —— 同时配 DM-A、DM-B 地址,驱动自动连当前主库,切换后应用自动重连,**无需改配置**
- 备选:为达梦主库再配一个 VIP(切换时由 dmwatcher/dmmonitor 触发漂移);
- 不推荐:应用写死单 IP(切换即断)。
---
## 6. 数据同步与一致性总表
| 组件 | 同步方式 | 方向 | RPO | 一致性 |
| --- | --- | --- | --- | --- |
| 达梦 DM8 | Redo 实时同步(MAL/守护) | 主→备 | ≈0 | 强一致 |
| Redis | 主从异步复制 + 哨兵 | 主→从 | 极小 | 最终一致 |
| FastDFS | Storage 同组互备 | 双向 | 极小 | 最终一致 |
| EMQX | Mnesia 集群同步 | 双向 | 极小 | 最终一致 |
| Nacos | Distro/Raft 集群同步 | 双向 | 极小 | 配置强/注册最终 |
| 前后端/Tengine | 部署期同步(同版本包) | — | — | 一致 |
---
## 7. 故障切换流程
| 故障 | 处理 | RTO | RPO |
| --- | --- | --- | --- |
| APP-1(应用主)宕 | VIP 3s 内飘 APP-2,整台接管 | 数秒 | 0 |
| 单个 MW 节点宕 | Nacos/EMQX 集群自愈;FastDFS 走另一 Storage;若主 Redis 宕→哨兵选从升主 | 秒级 | 极小 |
| Redis 主宕 | 3 哨兵投票(quorum=2)→从升主,SDK 自动重连 | 秒级 | 极小 |
| DM-A(主库)宕 | dmwatcher 检测→DM-MON 确认监视器仲裁→DM-B 升主;JDBC ep_list 自动连新主 | 10~60s | ≈0 |
| DM-MON(监视器)宕 | 不承载业务;仅使达梦自动切换退化为人工/延迟切换,业务不中断 | — | — |
| 计划内回切 | 低峰期 dmmonitor 手动 Switchover 回主库;Keepalived 视策略是否抢占回 APP-1 | — | — |
---
## 8. 资源与目录规划
沿用现场 `/data/services/` 约定:
| 节点 | 关键目录 |
| --- | --- |
| APP-1/2 | `/data/tengine``/data/services/api`(前后端)、`/data/keepalived` |
| MW-1/2/3 | `/data/nacos``/data/emqx``/data/fastdfs`(tracker±storage)、`/data/redis`(含哨兵) |
| DM-A/B | `/data/dmdbms``/data/dmdata` |
| DM-MON | `/data/dmmonitor` |
> 关键层独立:数据库 3 台独占、应用 2 台独占;中间件 3 台承载 4 套集群,建议 8C16G+ 并监控资源水位。
---
## 9. 监控与运维
| 对象 | 监控手段 | 告警阈值建议 |
| --- | --- | --- |
| VIP | VIP 存活与归属 | VIP 不可达→立即告警 |
| 达梦主备 | dmmonitor、归档同步延迟 | 备库延迟超阈、角色异常 |
| Redis 哨兵 | `redis-cli -p 26379 sentinel masters`、主从状态 | 主从断开、`s_down/o_down`、failover |
| Nacos | 控制台、节点健康 | 节点数 < 3、leader 缺失 |
| EMQX | Dashboard、节点状态 | 节点离线、连接骤降 |
| FastDFS | `fdfs_monitor` | Storage offline、同步延迟 |
> 复用项目现有服务自检/监测能力(`AuxiliaryTool/ScriptTool/ServiceSelfInspection`、`XTY-MONITOR`),扩展对**VIP 归属 / 达梦主备角色 / Redis 哨兵主从 / 集群节点数**的检查。
---
## 10. 风险评估与注意事项(国产化 / ARM / 麒麟 V10 专项)
| # | 风险 | 影响 | 应对 |
| --- | --- | --- | --- |
| 1 | ARM 架构组件可用性 | 部分中间件无官方 ARM 包 | 达梦/EMQX/Nacos(JVM) 支持 ARM;**Tengine、FastDFS、Redis 需在麒麟 V10 ARM 源码编译**,提前验证 |
| 2 | 麒麟 V10 软件源 | Keepalived/依赖缺失 | 配置麒麟官方源/本地源,离线备 rpm |
| 3 | 达梦切换后应用连旧主库 | 切库后写入失败 | **JDBC 主备 ep_list**(见 §5.4),驱动自动连新主 |
| 4 | Redis 单主无分片 | 写入集中一个节点 | 会议系统写入量够用;重负载时升级 Cluster |
| 5 | MW 资源争抢 | Redis 内存 vs FastDFS 磁盘 vs Nacos JVM | 8C16G+ 起步,监控水位,必要时拆分 |
| 6 | 脑裂(Split-Brain) | 双主致数据冲突 | 独立心跳网卡 + **DM-MON 确认监视器**仲裁 |
| 7 | 时钟漂移 | 集群/数据库异常 | 全节点强制 NTP/Chrony 对时 |
| 8 | 切换抖动 | 主恢复抢占二次切换 | 评估启用 `nopreempt` |
| 9 | 授权(License) | ARM 达梦/meeting 多节点都要 | 各节点按需申请/激活授权 |
---
## 11. 实施阶段建议(概览)
- [ ] **阶段 0**:8 节点 OS 装机、网络(双网卡)、时钟同步、依赖源、ARM 编译验证(Tengine/FastDFS/Redis)
- [ ] **阶段 1**:达梦主备(DM-A/DM-B)+ DM-MON 监视器,配置 MAL/守护,验证切换 + JDBC ep_list
- [ ] **阶段 2**:Redis 主从+3 哨兵(MW-1/2/3),验证 failover
- [ ] **阶段 3**:Nacos 3 节点 / EMQX 3 节点 / FastDFS(3 Tracker + 2 Storage 同组)
- [ ] **阶段 4**:APP-1/APP-2 部署 Tengine+前后端,Keepalived VIP
- [ ] **阶段 5**:联调与故障演练(逐节点关机/拔网线),记录 RTO/RPO
- [ ] **阶段 6**:监控接入、输出运维手册
---
## 12. 待确认事项(Open Questions)
1. **硬件**:8 节点是否均为物理机?是否提供独立心跳网卡?
2. **IP 网段**:现场业务网/心跳网网段与网关?
3. **Redis 规模**:1 主 2 从 + 3 哨兵够用?是否需持久化(AOF/RDB)策略与内存上限?未来是否预留 Cluster 升级空间?
4. **达梦接入**:确认使用 JDBC 主备 ep_list(推荐),还是为达梦另配 VIP?
5. **Tengine 版本**:锁定 3.1.0(麒麟 V10 ARM 自行编译,确认工具链)?
6. **切换策略**:Keepalived 是否启用抢占?
7. **授权**:达梦/meeting 各节点授权如何申请分发?
8. **Redis 归属**:虽由本团队部署,运维边界/资产归属是否需与甲方书面明确?
---
## 13. 结论
本方案以 **「三层冗余 + 主备/集群」** 为核心,共 **8 台服务器**
- **应用接入层(APP×2 主备 + VIP)**:Tengine + 前后端同机,备机整台秒级接管;
- **中间件层(MW×3 集群)**:Nacos 3 / EMQX 3 / FastDFS(3 Tracker+2 Storage) / **Redis 1 主 2 从 + 3 哨兵**,3 节点同时满足 Raft 与哨兵多数派;
- **数据库层(DM×3)**:达梦 DMDataWatch 主备(RPO≈0)+ **独立 DM-MON 确认监视器**,自动切换可靠、抗脑裂;
- **应用↔DB 用 JDBC 主备 ep_list**,切库后应用自动重连。
整体满足国产化、ARM 麒麟 V10 环境下的高可用需求:**RTO 接入/缓存层秒级、数据库层 10~60s**,RPO≈0;三层职责清晰、故障爆炸半径小,并具备可观测、可演练、可回切的运维特性。
---
### 文档变更记录
| 版本 | 日期 | 变更 | 作者 |
| --- | --- | --- | --- |
| v0.1 | 2026-07-03 | 初稿,两机热备架构分析 | — |
| v0.2 | 2026-07-03 | 拆分前后端、引入独立监视器节点 | — |
| v0.3 | 2026-07-03 | 重构为 8 节点分层(Redis Cluster) | — |
| v0.4 | 2026-07-03 | **定稿**:应用接入 2(主备+VIP) / 中间件 3(Nacos+EMQX+FastDFS+Redis**主从哨兵**) / 达梦 3(主备+监视器);补充 JDBC ep_list 故障切换、哨兵 quorum、风险与结论 | — |
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>新统一平台 高可用架构图(5节点·X86)</title>
<style>
* { box-sizing: border-box; }
body { margin:0; background:#eef2f7; font-family:"Microsoft YaHei","PingFang SC","Segoe UI",system-ui,sans-serif; color:#1f2a37; }
.wrap { max-width:1500px; margin:0 auto; padding:24px 16px 48px; }
.bar { display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; flex-wrap:wrap; gap:10px; }
.bar h1 { font-size:20px; margin:0; }
.bar .meta { font-size:13px; color:#5b6675; }
.btns button { background:#2563eb; color:#fff; border:0; border-radius:7px; padding:8px 14px; font-size:13px; cursor:pointer; margin-right:8px; }
.btns button.alt { background:#475569; }
.card { background:#fff; border-radius:14px; box-shadow:0 6px 24px rgba(15,23,42,.08); padding:12px; }
svg { width:100%; height:auto; display:block; }
.note { margin-top:14px; font-size:12.5px; color:#5b6675; line-height:1.8; background:#fff; border-radius:12px; padding:16px 20px; box-shadow:0 6px 24px rgba(15,23,42,.06); }
.note b { color:#1f2a37; }
@media print { body{background:#fff;} .btns{display:none;} .card{box-shadow:none;} }
</style>
</head>
<body>
<div class="wrap">
<div class="bar">
<div>
<h1>新统一平台 · 高可用架构图(5 节点 · X86)</h1>
<div class="meta">3 台通用机对称全栈 + 2 台达梦专用 | Redis 主从+哨兵 | 当前项目</div>
</div>
<div class="btns">
<button onclick="window.print()">打印 / 导出 PDF</button>
<button class="alt" onclick="downloadSvg()">导出 SVG</button>
</div>
</div>
<div class="card">
<svg viewBox="0 0 1400 1010" xmlns="http://www.w3.org/2000/svg" font-family="Microsoft YaHei, PingFang SC, sans-serif">
<defs>
<marker id="aB" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#1d4ed8"/></marker>
<marker id="aR" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#dc2626"/></marker>
<marker id="aA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#d97706"/></marker>
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#0f172a" flood-opacity="0.10"/></filter>
<clipPath id="cN1"><rect x="70" y="185" width="400" height="330" rx="12"/></clipPath>
<clipPath id="cN2"><rect x="520" y="185" width="400" height="330" rx="12"/></clipPath>
<clipPath id="cN3"><rect x="970" y="185" width="400" height="330" rx="12"/></clipPath>
<clipPath id="cD1"><rect x="300" y="605" width="400" height="180" rx="12"/></clipPath>
<clipPath id="cD2"><rect x="740" y="605" width="400" height="180" rx="12"/></clipPath>
</defs>
<rect x="0" y="0" width="1400" height="1010" fill="#f7f9fc"/>
<!-- 标题 -->
<text x="700" y="38" text-anchor="middle" font-size="24" font-weight="700" fill="#0f172a">新统一平台 · 高可用架构(5 节点 · X86)</text>
<text x="700" y="62" text-anchor="middle" font-size="13" fill="#64748b">3 台通用机对称全栈 + 2 台达梦专用 | Redis 主从 + 哨兵</text>
<!-- VIP -->
<rect x="545" y="82" width="310" height="42" rx="21" fill="#fffbeb" stroke="#d97706" stroke-width="2"/>
<text x="700" y="109" text-anchor="middle" font-size="15" font-weight="700" fill="#92400e">VIP · Keepalived 浮动入口</text>
<!-- VIP → 节点 -->
<path d="M 600 124 Q 430 150 272 183" fill="none" stroke="#1d4ed8" stroke-width="2.2" marker-end="url(#aB)"/>
<text x="350" y="150" font-size="11.5" font-weight="700" fill="#1d4ed8">默认</text>
<path d="M 700 124 L 700 183" fill="none" stroke="#94a3b8" stroke-width="1.6" stroke-dasharray="5 4"/>
<path d="M 800 124 Q 980 150 1168 183" fill="none" stroke="#94a3b8" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#aB)"/>
<text x="1010" y="150" font-size="11.5" font-weight="600" fill="#64748b">故障切换</text>
<!-- ===== Tier1 通用节点 ===== -->
<rect x="40" y="140" width="1320" height="400" rx="16" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1.5"/>
<text x="64" y="166" font-size="14" font-weight="700" fill="#334155">① 通用节点 ×3 · 对称全栈混部(应用接入 + Nacos / EMQX / FastDFS / Redis)</text>
<!-- N1 -->
<g filter="url(#shadow)"><rect x="70" y="185" width="400" height="330" rx="12" fill="#fff"/></g>
<g clip-path="url(#cN1)"><rect x="70" y="185" width="400" height="46" fill="#16a34a"/></g>
<rect x="70" y="185" width="400" height="330" rx="12" fill="none" stroke="#16a34a" stroke-width="2"/>
<text x="270" y="214" text-anchor="middle" font-size="15" font-weight="700" fill="#fff">N1 · 通用节点(主 · VIP MASTER)</text>
<g font-size="13" fill="#334155">
<rect x="92" y="252" width="11" height="11" rx="2" fill="#2563eb"/><text x="110" y="262">应用接入:Tengine + 前端 + 后端</text>
<rect x="92" y="282" width="11" height="11" rx="2" fill="#7c3aed"/><text x="110" y="292">Nacos #1(配置 / 注册)</text>
<rect x="92" y="312" width="11" height="11" rx="2" fill="#0d9488"/><text x="110" y="322">EMQX #1(MQTT)</text>
<rect x="92" y="342" width="11" height="11" rx="2" fill="#ea580c"/><text x="110" y="352">FastDFS Tracker #1 + Storage #1</text>
<rect x="92" y="372" width="11" height="11" rx="2" fill="#059669"/><text x="110" y="382">Redis:主 + 哨兵 #1</text>
</g>
<line x1="92" y1="406" x2="448" y2="406" stroke="#e2e8f0" stroke-width="1"/>
<text x="92" y="428" font-size="11.5" fill="#64748b">· 对外承接 VIP 流量</text>
<text x="92" y="446" font-size="11.5" fill="#64748b">· Redis 写入主 · Storage 副本 #1</text>
<text x="92" y="464" font-size="11.5" fill="#64748b">· Keepalived 优先级最高</text>
<!-- N2 -->
<g filter="url(#shadow)"><rect x="520" y="185" width="400" height="330" rx="12" fill="#fff"/></g>
<g clip-path="url(#cN2)"><rect x="520" y="185" width="400" height="46" fill="#64748b"/></g>
<rect x="520" y="185" width="400" height="330" rx="12" fill="none" stroke="#64748b" stroke-width="2"/>
<text x="720" y="214" text-anchor="middle" font-size="15" font-weight="700" fill="#fff">N2 · 通用节点(备 · VIP BACKUP)</text>
<g font-size="13" fill="#334155">
<rect x="542" y="252" width="11" height="11" rx="2" fill="#2563eb"/><text x="560" y="262">应用接入:Tengine + 前端 + 后端</text>
<rect x="542" y="282" width="11" height="11" rx="2" fill="#7c3aed"/><text x="560" y="292">Nacos #2(配置 / 注册)</text>
<rect x="542" y="312" width="11" height="11" rx="2" fill="#0d9488"/><text x="560" y="322">EMQX #2(MQTT)</text>
<rect x="542" y="342" width="11" height="11" rx="2" fill="#ea580c"/><text x="560" y="352">FastDFS Tracker #2 + Storage #2</text>
<rect x="542" y="372" width="11" height="11" rx="2" fill="#059669"/><text x="560" y="382">Redis:从 1 + 哨兵 #2</text>
</g>
<line x1="542" y1="406" x2="898" y2="406" stroke="#e2e8f0" stroke-width="1"/>
<text x="542" y="428" font-size="11.5" fill="#64748b">· 与 N1 同版本热备,随时接管</text>
<text x="542" y="446" font-size="11.5" fill="#64748b">· Redis 从 · Storage 副本 #2</text>
<text x="542" y="464" font-size="11.5" fill="#64748b">· FastDFS 同组互保</text>
<!-- N3 -->
<g filter="url(#shadow)"><rect x="970" y="185" width="400" height="330" rx="12" fill="#fff"/></g>
<g clip-path="url(#cN3)"><rect x="970" y="185" width="400" height="46" fill="#64748b"/></g>
<rect x="970" y="185" width="400" height="330" rx="12" fill="none" stroke="#64748b" stroke-width="2"/>
<text x="1170" y="214" text-anchor="middle" font-size="15" font-weight="700" fill="#fff">N3 · 通用节点(备 · VIP BACKUP)</text>
<g font-size="13" fill="#334155">
<rect x="992" y="252" width="11" height="11" rx="2" fill="#2563eb"/><text x="1010" y="262">应用接入:Tengine + 前端 + 后端</text>
<rect x="992" y="282" width="11" height="11" rx="2" fill="#7c3aed"/><text x="1010" y="292">Nacos #3(配置 / 注册)</text>
<rect x="992" y="312" width="11" height="11" rx="2" fill="#0d9488"/><text x="1010" y="322">EMQX #3(MQTT)</text>
<rect x="992" y="342" width="11" height="11" rx="2" fill="#ea580c"/><text x="1010" y="352">FastDFS Tracker #3 + Storage #3</text>
<rect x="992" y="372" width="11" height="11" rx="2" fill="#059669"/><text x="1010" y="382">Redis:从 2 + 哨兵 #3</text>
</g>
<line x1="992" y1="406" x2="1348" y2="406" stroke="#e2e8f0" stroke-width="1"/>
<text x="992" y="428" font-size="11.5" fill="#64748b">· 三节点对称,便于运维</text>
<text x="992" y="446" font-size="11.5" fill="#64748b">· 凑 Raft / 哨兵 quorum</text>
<text x="992" y="464" font-size="11.5" fill="#64748b">· FastDFS 三副本之一</text>
<!-- 节点间集群互连 -->
<line x1="470" y1="208" x2="520" y2="208" stroke="#7c3aed" stroke-width="1.6" stroke-dasharray="4 3"/>
<line x1="920" y1="208" x2="970" y2="208" stroke="#7c3aed" stroke-width="1.6" stroke-dasharray="4 3"/>
<text x="700" y="532" text-anchor="middle" font-size="12" font-weight="600" fill="#6d28d9">三机集群互连:Keepalived VRRP · Nacos(Raft) · EMQX(Mnesia) · FastDFS(同组同步) · Redis(主从 + 哨兵)</text>
<!-- ===== Tier2 达梦专用 ===== -->
<rect x="40" y="560" width="1320" height="250" rx="16" fill="#fef9f9" stroke="#fbc8c8" stroke-width="1.5"/>
<text x="64" y="586" font-size="14" font-weight="700" fill="#7f1d1d">② 达梦数据库 ×2(专用机 · 仅搭建达梦)· 确认监视器 dmmonitor 在备机 DM-B</text>
<!-- DM-A -->
<g filter="url(#shadow)"><rect x="300" y="605" width="400" height="180" rx="12" fill="#fff"/></g>
<g clip-path="url(#cD1)"><rect x="300" y="605" width="400" height="44" fill="#dc2626"/></g>
<rect x="300" y="605" width="400" height="180" rx="12" fill="none" stroke="#dc2626" stroke-width="2.2"/>
<text x="500" y="633" text-anchor="middle" font-size="15" font-weight="700" fill="#fff">DM-A · 达梦主库 PRIMARY</text>
<g font-size="13" fill="#334155">
<rect x="322" y="660" width="11" height="11" rx="2" fill="#dc2626"/><text x="340" y="670">达梦 DM8 · 主库(读写)</text>
<rect x="322" y="688" width="11" height="11" rx="2" fill="#dc2626"/><text x="340" y="698">dmwatcher 守护进程</text>
</g>
<line x1="322" y1="722" x2="678" y2="722" stroke="#fee2e2" stroke-width="1"/>
<text x="322" y="744" font-size="11.5" fill="#64748b">· Redo 实时同步 → 备库 · RPO≈0</text>
<text x="322" y="762" font-size="11.5" fill="#64748b">· 应用 JDBC 主备 ep_list 接入</text>
<!-- DM-B -->
<g filter="url(#shadow)"><rect x="740" y="605" width="400" height="180" rx="12" fill="#fff"/></g>
<g clip-path="url(#cD2)"><rect x="740" y="605" width="400" height="44" fill="#475569"/></g>
<rect x="740" y="605" width="400" height="180" rx="12" fill="none" stroke="#475569" stroke-width="2.2"/>
<text x="940" y="633" text-anchor="middle" font-size="15" font-weight="700" fill="#fff">DM-B · 备库 STANDBY + dmmonitor</text>
<g font-size="13" fill="#334155">
<rect x="762" y="660" width="11" height="11" rx="2" fill="#dc2626"/><text x="780" y="670">达梦 DM8 · 备库(只读同步)</text>
<rect x="762" y="688" width="11" height="11" rx="2" fill="#dc2626"/><text x="780" y="698">dmwatcher 守护进程</text>
<rect x="762" y="716" width="11" height="11" rx="2" fill="#d97706"/><text x="780" y="726">dmmonitor 确认监视器(仲裁)</text>
</g>
<line x1="762" y1="750" x2="1118" y2="750" stroke="#e2e8f0" stroke-width="1"/>
<text x="762" y="772" font-size="11.5" fill="#64748b">· 主库故障提升为主(10~60s)</text>
<!-- DM-A → DM-B Redo -->
<line x1="700" y1="690" x2="738" y2="690" stroke="#dc2626" stroke-width="2.4" marker-end="url(#aR)"/>
<text x="719" y="682" text-anchor="middle" font-size="11" font-weight="700" fill="#b91c1c">Redo</text>
<!-- DM-B dmmonitor → DM-A 监控/仲裁 -->
<line x1="740" y1="725" x2="702" y2="725" stroke="#d97706" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#aA)"/>
<text x="721" y="745" text-anchor="middle" font-size="10.5" font-weight="700" fill="#b45309">监控/仲裁</text>
<!-- 跨层:应用 → 达梦主库(左侧绕行) -->
<path d="M 70 350 L 52 350 L 52 690 L 298 690" fill="none" stroke="#1d4ed8" stroke-width="1.8" marker-end="url(#aB)"/>
<text x="40" y="520" font-size="11.5" fill="#1d4ed8" transform="rotate(-90 40 520)">前后端 → 达梦主库读写(JDBC 主备 ep_list)</text>
<!-- ===== 图例 ===== -->
<rect x="40" y="830" width="1320" height="150" rx="12" fill="#fff" stroke="#e2e8f0" stroke-width="1.5"/>
<text x="64" y="856" font-size="13" font-weight="700" fill="#334155">图例</text>
<g font-size="12" fill="#374151">
<rect x="64" y="874" width="13" height="13" rx="3" fill="#dbeafe" stroke="#2563eb"/><text x="84" y="885">应用接入(Tengine+前后端)</text>
<rect x="290" y="874" width="13" height="13" rx="3" fill="#ede9fe" stroke="#7c3aed"/><text x="310" y="885">Nacos</text>
<rect x="400" y="874" width="13" height="13" rx="3" fill="#ccfbf1" stroke="#0d9488"/><text x="420" y="885">EMQX</text>
<rect x="510" y="874" width="13" height="13" rx="3" fill="#ffedd5" stroke="#ea580c"/><text x="530" y="885">FastDFS</text>
<rect x="630" y="874" width="13" height="13" rx="3" fill="#d1fae5" stroke="#059669"/><text x="650" y="885">Redis(主从+哨兵)</text>
<rect x="820" y="874" width="13" height="13" rx="3" fill="#fee2e2" stroke="#dc2626"/><text x="840" y="885">达梦 DM8</text>
<rect x="960" y="874" width="13" height="13" rx="3" fill="#fef3c7" stroke="#d97706"/><text x="980" y="885">dmmonitor 仲裁</text>
<rect x="1130" y="874" width="13" height="13" rx="3" fill="#fffbeb" stroke="#d97706"/><text x="1150" y="885">Keepalived / VIP</text>
</g>
<g font-size="12" fill="#374151">
<line x1="64" y1="916" x2="104" y2="916" stroke="#1d4ed8" stroke-width="2" marker-end="url(#aB)"/><text x="112" y="920">业务流量 / 读写访问</text>
<line x1="300" y1="916" x2="340" y2="916" stroke="#dc2626" stroke-width="2.2" marker-end="url(#aR)"/><text x="348" y="920">数据实时同步(主→备)</text>
<line x1="560" y1="916" x2="600" y2="916" stroke="#7c3aed" stroke-width="1.8" stroke-dasharray="6 4"/><text x="608" y="920">集群互连 / 同步</text>
<line x1="780" y1="916" x2="820" y2="916" stroke="#d97706" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#aA)"/><text x="828" y="920">达梦监视 / 仲裁</text>
</g>
<line x1="64" y1="948" x2="1336" y2="948" stroke="#f1f5f9" stroke-width="1"/>
<text x="64" y="970" font-size="11.5" fill="#94a3b8">读图:① VIP 默认指向 N1(主),故障时在 N1/N2/N3 间浮动;② 三通用节点对称、单机故障可容忍(集群剩 2/3 仍满足多数派);③ 达梦主备实时同步 RPO≈0,切库 10~60s;④ 应用经 JDBC 主备 ep_list 自动连新主。</text>
</svg>
</div>
<div class="note">
<b>读图要点(5 节点 · X86):</b><br>
<b>3 台通用节点(N1/N2/N3)对称全栈混部</b>:每台都跑 应用接入(Tengine+前端+后端) + Nacos + EMQX + FastDFS(Tracker+Storage) + Redis(主/从+哨兵)。三节点完全对称,便于运维。<br>
<b>角色分工</b>:N1=VIP 主+Redis 主+Storage#1;N2=备+Redis 从1+Storage#2;N3=备+Redis 从2+Storage#3 → <b>FastDFS 三副本</b><br>
<b>VIP 浮动</b>:Keepalived 在 N1/N2/N3 间浮动,任一通用机故障,VIP 秒级飘到存活节点。<br>
<b>2 台达梦专用机(DM-A 主 / DM-B 备)</b>:仅跑达梦;<b>dmmonitor 确认监视器部署在备机 DM-B</b>(主机宕时备机存活可正常仲裁)。<br>
<b>单通用机故障可容忍</b>:Nacos/EMQX 剩 2/3 仍满足多数派;Redis 哨兵选从升主;FastDFS 另两 Storage 有副本;VIP 飘移。<br>
<b>应用→达梦用 JDBC 主备 ep_list</b>,切库后自动连新主。<br>
⚠️ <b>权衡</b>:① 通用机负载较重(建议 16C32G+);② dmmonitor 在 DM-B(非独立第三方),网络脑裂时仲裁弱于独立节点(靠独立心跳网卡缓解);若更看重抗脑裂可改放 N1/N2。资源充裕可用 <code>双机热备架构图_8节点全分层版.html</code>
</div>
</div>
<script>
function downloadSvg(){
const svg = document.querySelector('svg');
const xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + new XMLSerializer().serializeToString(svg);
const blob = new Blob([xml], {type:'image/svg+xml'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = '高可用架构图_5节点_X86.svg';
a.click();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>新统一平台 高可用架构图(定稿·8节点三层)</title>
<style>
* { box-sizing: border-box; }
body { margin:0; background:#eef2f7; font-family:"Microsoft YaHei","PingFang SC","Segoe UI",system-ui,sans-serif; color:#1f2a37; }
.wrap { max-width:1560px; margin:0 auto; padding:24px 16px 48px; }
.bar { display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; flex-wrap:wrap; gap:10px; }
.bar h1 { font-size:20px; margin:0; }
.bar .meta { font-size:13px; color:#5b6675; }
.btns button { background:#2563eb; color:#fff; border:0; border-radius:7px; padding:8px 14px; font-size:13px; cursor:pointer; margin-right:8px; }
.btns button.alt { background:#475569; }
.card { background:#fff; border-radius:14px; box-shadow:0 6px 24px rgba(15,23,42,.08); padding:10px; }
svg { width:100%; height:auto; display:block; }
.note { margin-top:14px; font-size:12.5px; color:#5b6675; line-height:1.75; background:#fff; border-radius:12px; padding:14px 18px; box-shadow:0 6px 24px rgba(15,23,42,.06); }
.note b { color:#1f2a37; }
@media print { body{background:#fff;} .btns{display:none;} .card{box-shadow:none;} }
</style>
</head>
<body>
<div class="wrap">
<div class="bar">
<div>
<h1>新统一平台 · 高可用架构图(定稿 · 8 节点三层)</h1>
<div class="meta">环境:ARM + 麒麟 V10 | 应用接入(主备+VIP) + 中间件(3节点集群) + 达梦(主备+独立监视器) | Redis 主从+哨兵</div>
</div>
<div class="btns">
<button onclick="window.print()">打印 / 导出 PDF</button>
<button class="alt" onclick="downloadSvg()">导出 SVG</button>
</div>
</div>
<div class="card">
<svg viewBox="0 0 1500 1100" xmlns="http://www.w3.org/2000/svg" font-family="Microsoft YaHei, PingFang SC, sans-serif">
<defs>
<marker id="aB" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#1d4ed8"/></marker>
<marker id="aG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#94a3b8"/></marker>
<marker id="aR" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#c62828"/></marker>
<marker id="aT" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#0f766e"/></marker>
<marker id="aA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#ef6c00"/></marker>
<marker id="aP" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#6d28d9"/></marker>
<clipPath id="cA1"><rect x="240" y="250" width="440" height="200" rx="12"/></clipPath>
<clipPath id="cA2"><rect x="820" y="250" width="440" height="200" rx="12"/></clipPath>
<clipPath id="cM1"><rect x="70" y="520" width="440" height="210" rx="12"/></clipPath>
<clipPath id="cM2"><rect x="530" y="520" width="440" height="210" rx="12"/></clipPath>
<clipPath id="cM3"><rect x="990" y="520" width="440" height="210" rx="12"/></clipPath>
<clipPath id="cD1"><rect x="120" y="810" width="380" height="160" rx="12"/></clipPath>
<clipPath id="cD2"><rect x="540" y="810" width="380" height="160" rx="12"/></clipPath>
<clipPath id="cDM"><rect x="980" y="810" width="400" height="160" rx="12"/></clipPath>
</defs>
<rect x="0" y="0" width="1500" height="1100" fill="#f7f9fc"/>
<text x="750" y="40" text-anchor="middle" font-size="24" font-weight="700" fill="#0f172a">新统一平台 · 高可用架构(定稿 · 8 节点三层冗余)</text>
<text x="750" y="63" text-anchor="middle" font-size="13" fill="#64748b">应用接入(2·主备+VIP) + 中间件(3·集群) + 达梦(3·主备+监视器) | ARM · 麒麟 V10</text>
<!-- 用户 -->
<rect x="625" y="80" width="250" height="40" rx="9" fill="#eef2ff" stroke="#4338ca" stroke-width="2"/>
<text x="750" y="106" text-anchor="middle" font-size="14" font-weight="600" fill="#3730a3">用户 / 终端 / IoT 设备</text>
<line x1="750" y1="120" x2="750" y2="144" stroke="#1d4ed8" stroke-width="2.2" marker-end="url(#aB)"/>
<!-- VIP -->
<rect x="485" y="146" width="530" height="44" rx="10" fill="#fff7e0" stroke="#f59e0b" stroke-width="2.4"/>
<text x="750" y="173" text-anchor="middle" font-size="14.5" font-weight="700" fill="#b45309">VIP 192.168.9.100 · Keepalived VRRP(APP-1 / APP-2 间浮动)</text>
<path d="M 600 190 Q 530 215 460 248" fill="none" stroke="#1d4ed8" stroke-width="2.4" marker-end="url(#aB)"/>
<text x="455" y="218" font-size="12" font-weight="700" fill="#1d4ed8">默认路由</text>
<path d="M 900 190 Q 970 215 1040 248" fill="none" stroke="#94a3b8" stroke-width="2" stroke-dasharray="7 5" marker-end="url(#aG)"/>
<text x="975" y="218" font-size="12" font-weight="600" fill="#64748b">故障切换</text>
<!-- ===== Band1 应用接入 ===== -->
<rect x="30" y="210" width="1440" height="250" rx="14" fill="#f1f5f9" stroke="#cbd5e1" stroke-width="1.5"/>
<text x="52" y="234" font-size="14" font-weight="700" fill="#334155">① 应用接入层(2 节点 · 主备 · Tengine + 前后端同机 · VIP 跟随)</text>
<!-- APP-1 -->
<g clip-path="url(#cA1)"><rect x="240" y="250" width="440" height="200" fill="#fff"/><rect x="240" y="250" width="440" height="38" fill="#16a34a"/></g>
<rect x="240" y="250" width="440" height="200" rx="12" fill="none" stroke="#16a34a" stroke-width="2.5"/>
<text x="460" y="275" text-anchor="middle" font-size="14.5" font-weight="700" fill="#fff">APP-1 · 192.168.9.10 · 应用接入 · 主(VIP MASTER)</text>
<g font-size="13" fill="#374151">
<rect x="262" y="306" width="11" height="11" rx="2" fill="#1976d2"/><text x="280" y="316">Tengine 3.1.0(接入 · 负载均衡)</text>
<rect x="262" y="332" width="11" height="11" rx="2" fill="#2e7d32"/><text x="280" y="342">前端服务(Web 前台 / 后台)</text>
<rect x="262" y="358" width="11" height="11" rx="2" fill="#00838f"/><text x="280" y="368">后端服务(API)java-meeting · python-cmdb · python-voice</text>
<rect x="262" y="384" width="11" height="11" rx="2" fill="#f59e0b"/><text x="280" y="394">Keepalived(VIP 跟随主备切换)</text>
<text x="262" y="426" font-size="11.5" fill="#64748b">对外承接 VIP 流量,承载全部前后端业务</text>
</g>
<!-- APP-2 -->
<g clip-path="url(#cA2)"><rect x="820" y="250" width="440" height="200" fill="#fff"/><rect x="820" y="250" width="440" height="38" fill="#64748b"/></g>
<rect x="820" y="250" width="440" height="200" rx="12" fill="none" stroke="#64748b" stroke-width="2.5"/>
<text x="1040" y="275" text-anchor="middle" font-size="14.5" font-weight="700" fill="#fff">APP-2 · 192.168.9.11 · 应用接入 · 备(VIP BACKUP)</text>
<g font-size="13" fill="#374151">
<rect x="842" y="306" width="11" height="11" rx="2" fill="#1976d2"/><text x="860" y="316">Tengine 3.1.0(接入 · 负载均衡)</text>
<rect x="842" y="332" width="11" height="11" rx="2" fill="#2e7d32"/><text x="860" y="342">前端服务(Web 前台 / 后台)</text>
<rect x="842" y="358" width="11" height="11" rx="2" fill="#00838f"/><text x="860" y="368">后端服务(API)java-meeting · python-cmdb · python-voice</text>
<rect x="842" y="384" width="11" height="11" rx="2" fill="#f59e0b"/><text x="860" y="394">Keepalived(VIP 跟随主备切换)</text>
<text x="842" y="426" font-size="11.5" fill="#64748b">与 APP-1 同版本热备,主机故障时整台接管</text>
</g>
<!-- APP-1 ↔ APP-2 -->
<line x1="680" y1="300" x2="820" y2="300" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="6 4"/>
<text x="750" y="295" text-anchor="middle" font-size="11.5" fill="#b45309">Keepalived VRRP 心跳</text>
<!-- ===== Band2 中间件 ===== -->
<rect x="30" y="480" width="1440" height="270" rx="14" fill="#f5f3ff" stroke="#ddd6fe" stroke-width="1.5"/>
<text x="52" y="504" font-size="14" font-weight="700" fill="#4c1d95">② 中间件层(3 节点 · Nacos/EMQX 集群 · FastDFS 3Tracker+2Storage · Redis 1主2从+3哨兵)</text>
<!-- MW-1 -->
<g clip-path="url(#cM1)"><rect x="70" y="520" width="440" height="210" fill="#fff"/><rect x="70" y="520" width="440" height="38" fill="#4f46e5"/></g>
<rect x="70" y="520" width="440" height="210" rx="12" fill="none" stroke="#4f46e5" stroke-width="2.5"/>
<text x="290" y="545" text-anchor="middle" font-size="14.5" font-weight="700" fill="#fff">MW-1 · 192.168.9.20 · 中间件节点</text>
<g font-size="13" fill="#374151">
<rect x="92" y="576" width="11" height="11" rx="2" fill="#8e24aa"/><text x="110" y="586">Nacos #1(配置/注册)</text>
<rect x="92" y="602" width="11" height="11" rx="2" fill="#00897b"/><text x="110" y="612">EMQX #1(MQTT)</text>
<rect x="92" y="628" width="11" height="11" rx="2" fill="#f4511e"/><text x="110" y="638">FastDFS Tracker #1 + Storage #1</text>
<rect x="92" y="654" width="11" height="11" rx="2" fill="#0f766e"/><text x="110" y="664">Redis:主(Master) + Sentinel #1</text>
<text x="92" y="700" font-size="11.5" fill="#64748b">Redis 主 + 哨兵1(写入入口)</text>
</g>
<!-- MW-2 -->
<g clip-path="url(#cM2)"><rect x="530" y="520" width="440" height="210" fill="#fff"/><rect x="530" y="520" width="440" height="38" fill="#4f46e5"/></g>
<rect x="530" y="520" width="440" height="210" rx="12" fill="none" stroke="#4f46e5" stroke-width="2.5"/>
<text x="750" y="545" text-anchor="middle" font-size="14.5" font-weight="700" fill="#fff">MW-2 · 192.168.9.21 · 中间件节点</text>
<g font-size="13" fill="#374151">
<rect x="552" y="576" width="11" height="11" rx="2" fill="#8e24aa"/><text x="570" y="586">Nacos #2(配置/注册)</text>
<rect x="552" y="602" width="11" height="11" rx="2" fill="#00897b"/><text x="570" y="612">EMQX #2(MQTT)</text>
<rect x="552" y="628" width="11" height="11" rx="2" fill="#f4511e"/><text x="570" y="638">FastDFS Tracker #2 + Storage #2</text>
<rect x="552" y="654" width="11" height="11" rx="2" fill="#0f766e"/><text x="570" y="664">Redis:从1(Slave) + Sentinel #2</text>
<text x="552" y="700" font-size="11.5" fill="#64748b">Storage 同组互备 · Redis 从</text>
</g>
<!-- MW-3 -->
<g clip-path="url(#cM3)"><rect x="990" y="520" width="440" height="210" fill="#fff"/><rect x="990" y="520" width="440" height="38" fill="#4f46e5"/></g>
<rect x="990" y="520" width="440" height="210" rx="12" fill="none" stroke="#4f46e5" stroke-width="2.5"/>
<text x="1210" y="545" text-anchor="middle" font-size="14.5" font-weight="700" fill="#fff">MW-3 · 192.168.9.22 · 中间件节点</text>
<g font-size="13" fill="#374151">
<rect x="1012" y="576" width="11" height="11" rx="2" fill="#8e24aa"/><text x="1030" y="586">Nacos #3(配置/注册)</text>
<rect x="1012" y="602" width="11" height="11" rx="2" fill="#00897b"/><text x="1030" y="612">EMQX #3(MQTT)</text>
<rect x="1012" y="628" width="11" height="11" rx="2" fill="#f4511e"/><text x="1030" y="638">FastDFS Tracker #3</text>
<rect x="1012" y="654" width="11" height="11" rx="2" fill="#0f766e"/><text x="1030" y="664">Redis:从2(Slave) + Sentinel #3</text>
<text x="1012" y="700" font-size="11.5" fill="#64748b">3 节点凑 Raft/哨兵 quorum=2</text>
</g>
<!-- MW 集群互连 -->
<line x1="510" y1="540" x2="530" y2="540" stroke="#6d28d9" stroke-width="1.6" stroke-dasharray="4 3"/>
<line x1="970" y1="540" x2="990" y2="540" stroke="#6d28d9" stroke-width="1.6" stroke-dasharray="4 3"/>
<text x="750" y="745" text-anchor="middle" font-size="11.5" font-weight="600" fill="#6d28d9">集群互连:Nacos(Raft) · EMQX(Mnesia) · FastDFS(同组同步) · Redis(主从复制+哨兵仲裁)</text>
<!-- ===== Band3 数据库 ===== -->
<rect x="30" y="770" width="1440" height="210" rx="14" fill="#fef6f6" stroke="#e6b8b8" stroke-width="1.5"/>
<text x="52" y="794" font-size="14" font-weight="700" fill="#7f1d1d">③ 数据库层(3 节点 · 达梦 DMDataWatch · 主备 + 独立确认监视器)</text>
<!-- DM-A -->
<g clip-path="url(#cD1)"><rect x="120" y="810" width="380" height="160" fill="#fff"/><rect x="120" y="810" width="380" height="38" fill="#c62828"/></g>
<rect x="120" y="810" width="380" height="160" rx="12" fill="none" stroke="#c62828" stroke-width="2.6"/>
<text x="310" y="835" text-anchor="middle" font-size="14.5" font-weight="700" fill="#fff">DM-A · 192.168.9.30 · 达梦主库 PRIMARY</text>
<g font-size="13" fill="#374151">
<rect x="142" y="866" width="11" height="11" rx="2" fill="#c62828"/><text x="160" y="876">达梦 DM8 · 主库(读写)</text>
<rect x="142" y="892" width="11" height="11" rx="2" fill="#c62828"/><text x="160" y="902">dmwatcher 守护进程</text>
<text x="142" y="934" font-size="11.5" fill="#64748b">Redo 实时同步至备库 · RPO≈0</text>
<text x="142" y="954" font-size="11.5" fill="#64748b">应用 JDBC 主备 ep_list 接入</text>
</g>
<!-- DM-B -->
<g clip-path="url(#cD2)"><rect x="540" y="810" width="380" height="160" fill="#fff"/><rect x="540" y="810" width="380" height="38" fill="#a0a0a0"/></g>
<rect x="540" y="810" width="380" height="160" rx="12" fill="none" stroke="#c62828" stroke-width="2" stroke-dasharray="6 4"/>
<text x="730" y="835" text-anchor="middle" font-size="14.5" font-weight="700" fill="#7f1d1d">DM-B · 192.168.9.31 · 达梦备库 STANDBY</text>
<g font-size="13" fill="#374151">
<rect x="562" y="866" width="11" height="11" rx="2" fill="#c62828"/><text x="580" y="876">达梦 DM8 · 备库(只读同步)</text>
<rect x="562" y="892" width="11" height="11" rx="2" fill="#c62828"/><text x="580" y="902">dmwatcher 守护进程</text>
<text x="562" y="934" font-size="11.5" fill="#64748b">主库故障时提升为主</text>
<text x="562" y="954" font-size="11.5" fill="#64748b">切换耗时约 10~60s(RTO 瓶颈)</text>
</g>
<!-- DM-MON -->
<g clip-path="url(#cDM)"><rect x="980" y="810" width="400" height="160" fill="#fff"/><rect x="980" y="810" width="400" height="38" fill="#475569"/></g>
<rect x="980" y="810" width="400" height="160" rx="12" fill="none" stroke="#475569" stroke-width="2.5"/>
<text x="1180" y="835" text-anchor="middle" font-size="14.5" font-weight="700" fill="#fff">DM-MON · 192.168.9.32 · 达梦监视器(独立)</text>
<g font-size="13" fill="#374151">
<rect x="1002" y="866" width="11" height="11" rx="2" fill="#ef6c00"/><text x="1020" y="876">dmmonitor 确认监视器</text>
<rect x="1002" y="892" width="11" height="11" rx="2" fill="#ef6c00"/><text x="1020" y="902">监控主备 · 故障仲裁</text>
<text x="1002" y="934" font-size="11.5" fill="#64748b">保障自动切换 · 抗脑裂</text>
<text x="1002" y="954" font-size="11.5" fill="#64748b">独立于主备两机,不承载业务</text>
</g>
<!-- DM-A → DM-B Redo -->
<line x1="500" y1="890" x2="538" y2="890" stroke="#c62828" stroke-width="2.4" marker-end="url(#aR)"/>
<text x="519" y="882" text-anchor="middle" font-size="11" font-weight="700" fill="#b71c1c">Redo</text>
<!-- DM-MON → DM-A / DM-B 监控 -->
<path d="M 980 880 Q 760 855 500 875" fill="none" stroke="#ef6c00" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#aA)"/>
<path d="M 980 905 Q 870 920 920 905" fill="none" stroke="#ef6c00" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#aA)"/>
<text x="730" y="800" text-anchor="middle" font-size="11" font-weight="600" fill="#e65100">dmmonitor 监控主备 · 故障仲裁(确认监视器)</text>
<!-- 跨层:应用 → 中间件(右侧绕行) -->
<path d="M 1260 360 L 1430 360 L 1430 620 L 1430 740" fill="none" stroke="#1d4ed8" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#aB)"/>
<text x="1442" y="540" font-size="11.5" fill="#1d4ed8" transform="rotate(-90 1442 540)">应用 → 中间件(Nacos/Redis/EMQX/FastDFS)</text>
<!-- 跨层:应用 → 达梦主库(左侧绕行) -->
<path d="M 240 360 L 60 360 L 60 890 L 118 890" fill="none" stroke="#1d4ed8" stroke-width="1.8" marker-end="url(#aB)"/>
<text x="48" y="620" font-size="11.5" fill="#1d4ed8" transform="rotate(-90 48 620)">前后端 → 达梦主库读写(JDBC 主备 ep_list)</text>
<!-- ===== 图例 ===== -->
<rect x="30" y="1000" width="1440" height="86" rx="12" fill="#fff" stroke="#e2e8f0" stroke-width="1.5"/>
<text x="50" y="1023" font-size="13" font-weight="700" fill="#334155">图例</text>
<g font-size="11.5" fill="#374151">
<rect x="50" y="1035" width="13" height="13" rx="3" fill="#e8f1fb" stroke="#1976d2"/><text x="69" y="1046">Tengine/接入</text>
<rect x="185" y="1035" width="13" height="13" rx="3" fill="#e7f4e9" stroke="#2e7d32"/><text x="204" y="1046">前端</text>
<rect x="265" y="1035" width="13" height="13" rx="3" fill="#e0f7fa" stroke="#00838f"/><text x="284" y="1046">后端</text>
<rect x="345" y="1035" width="13" height="13" rx="3" fill="#fff7e0" stroke="#f59e0b"/><text x="364" y="1046">Keepalived/VIP</text>
<rect x="490" y="1035" width="13" height="13" rx="3" fill="#f3e9f8" stroke="#8e24aa"/><text x="509" y="1046">Nacos</text>
<rect x="585" y="1035" width="13" height="13" rx="3" fill="#e1f3f0" stroke="#00897b"/><text x="604" y="1046">EMQX</text>
<rect x="670" y="1035" width="13" height="13" rx="3" fill="#fcece4" stroke="#f4511e"/><text x="689" y="1046">FastDFS</text>
<rect x="780" y="1035" width="13" height="13" rx="3" fill="#e0f2f1" stroke="#0f766e"/><text x="799" y="1046">Redis(主从+哨兵)</text>
<rect x="945" y="1035" width="13" height="13" rx="3" fill="#fdecea" stroke="#c62828"/><text x="964" y="1046">达梦 DM8</text>
<rect x="1070" y="1035" width="13" height="13" rx="3" fill="#fff3e0" stroke="#ef6c00"/><text x="1089" y="1046">dmmonitor 仲裁</text>
</g>
<g font-size="11.5" fill="#374151">
<line x1="50" y1="1070" x2="88" y2="1070" stroke="#1d4ed8" stroke-width="2" marker-end="url(#aB)"/><text x="96" y="1074">业务流量/读写访问</text>
<line x1="270" y1="1070" x2="308" y2="1070" stroke="#c62828" stroke-width="2.2" marker-end="url(#aR)"/><text x="316" y="1074">数据实时同步(主→备)</text>
<line x1="510" y1="1070" x2="548" y2="1070" stroke="#6d28d9" stroke-width="1.8" stroke-dasharray="6 4"/><text x="556" y="1074">集群互连/同步</text>
<line x1="690" y1="1070" x2="728" y2="1070" stroke="#ef6c00" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#aA)"/><text x="736" y="1074">达梦监视/仲裁</text>
<line x1="870" y1="1070" x2="908" y2="1070" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="6 4"/><text x="916" y="1074">VRRP心跳</text>
</g>
</svg>
</div>
<div class="note">
<b>读图要点(定稿 · 8 节点三层):</b><br>
<b>应用接入层(APP-1 主 / APP-2 备)</b>:每台同机跑 <b>Tengine + 前端 + 后端</b>,Keepalived VIP 在两机间浮动;主机故障时备机<b>整台接管</b>(RTO 数秒)。<br>
<b>中间件层(MW-1/2/3)</b>:3 节点承载 Nacos 集群、EMQX 集群、FastDFS(3 Tracker + 2 Storage 同组互备)、Redis(<b>1 主 2 从 + 3 哨兵</b>,quorum=2)。3 节点同时满足 Nacos Raft 与 Redis 哨兵的多数派要求。<br>
<b>数据库层(DM-A 主 / DM-B 备 / DM-MON 监视器)</b>:达梦 DMDataWatch 主备实时同步(RPO≈0);独立 DM-MON 跑确认监视器,自动切换 + 抗脑裂;切换 10~60s(RTO 瓶颈)。<br>
<b>应用 → 中间件</b>:前后端访问 Nacos/Redis/EMQX/FastDFS(集群地址/哨兵地址)。<br>
<b>应用 → 达梦</b>:用 <b>JDBC 主备 ep_list 连接串</b>,主库切换后驱动自动连新主库,应用无需改配置。<br>
⑥ Redis 为主从+哨兵(1 主单点写入,无分片);若未来缓存写很重/数据量很大,可平滑升级为 Redis Cluster 分片。
</div>
</div>
<script>
function downloadSvg(){
const svg = document.querySelector('svg');
const xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + new XMLSerializer().serializeToString(svg);
const blob = new Blob([xml], {type:'image/svg+xml'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = '高可用架构图_定稿8节点.svg';
a.click();
}
</script>
</body>
</html>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论