【架构实战】多机房容灾架构设计方案

一、为什么需要多机房容灾

单机房部署存在诸多风险:

  • 硬件故障:服务器、交换机、存储设备故障
  • 电力中断:UPS电池耗尽、发电机故障
  • 网络故障:光纤被挖断、运营商故障
  • 自然灾害:火灾、水灾、地震等不可抗力
  • 人为失误:运维操作失误导致服务中断

多机房容灾的目标:

  • RTO(恢复时间目标):接近0
  • RPO(恢复点目标):数据丢失接近0

二、多机房架构模式

1. 主备模式(Active-Standby)

复制代码
主机房(Active) ← 同步复制 → 备机房(Standby)
     ↓                              ↓
   读写请求                    只读/热备

特点:

  • 架构简单,易于理解和维护
  • 资源利用率低(备机房闲置)
  • 故障切换需要手动或自动触发
  • 适合对可用性要求较高的核心系统

适用场景:

  • 核心数据库
  • 关键业务系统
  • 资金交易系统

2. 双活模式(Active-Active)

复制代码
机房A(Active) ←→ 机房B(Active)
    ↓                  ↓
  东部用户          西部用户

特点:

  • 两个机房同时对外提供服务
  • 资源利用率高
  • 数据同步是关键挑战
  • 任一机房故障,另一机房承载全部流量

技术挑战:

  • 跨机房网络延迟(10-50ms)
  • 数据一致性保证
  • 请求路由的准确性

3. 多活模式(Multi-Active)

复制代码
机房A ←→ 机房B ←→ 机房C ←→ 机房D
  ↓        ↓        ↓        ↓
 流量1    流量2    流量3    流量4

特点:

  • 多个机房同时提供服务
  • 按地域、用户特征分配流量
  • 最高级别的可用性保障
  • 复杂度也最高

三、数据同步方案

1. MySQL主主同步

主主复制(Master-Master)实现双向同步:

ini 复制代码
# 机房A - my.cnf配置
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
gtid-mode = ON
enforce-gtid-consistency = ON
auto-increment-offset = 1
auto-increment-increment = 2
relay-log = relay-bin
ini 复制代码
# 机房B - my.cnf配置
[mysqld]
server-id = 2
log-bin = mysql-bin
binlog-format = ROW
gtid-mode = ON
enforce-gtid-consistency = ON
auto-increment-offset = 2
auto-increment-increment = 2
relay-log = relay-bin

建立复制关系:

sql 复制代码
-- 机房A配置
CHANGE MASTER TO
    MASTER_HOST = '192.168.1.101',
    MASTER_USER = 'repl',
    MASTER_PASSWORD = 'password',
    MASTER_PORT = 3306,
    MASTER_AUTO_POSITION = 1;

START SLAVE;

-- 机房B配置(对称配置)
CHANGE MASTER TO
    MASTER_HOST = '192.168.1.100',
    MASTER_USER = 'repl',
    MASTER_PASSWORD = 'password',
    MASTER_PORT = 3306,
    MASTER_AUTO_POSITION = 1;

START SLAVE;

2. Redis主从同步

Redis原生支持主从复制,跨机房场景:

conf 复制代码
# 机房A Redis配置
bind 0.0.0.0
port 6379
masterauth "repl_password"
requirepass "redis_password"

# 开启AOF持久化
appendonly yes
appendfsync everysec
bash 复制代码
# 机房A添加从节点(机房B)
redis-cli -h 192.168.1.100 REPLICAOF 192.168.1.101 6379

重要配置:

conf 复制代码
# 适当调大超时时间,应对跨机房网络抖动
repl-timeout 120

# 启用无磁盘复制(减少网络开销)
repl-diskless-sync yes
repl-diskless-sync-delay 5

3. 消息队列同步

使用Kafka MirrorMaker实现跨机房消息同步:

yaml 复制代码
# Kafka MirrorMaker配置
version: '3'
services:
  mm:
    image: confluentinc/cp-kafka-mirror-maker:latest
    environment:
      BOOTSTRAP_SERVERS: "kafka-a:9092"
      TOPICS: ".*"
      WHITELIST: ".*"
      MIRROR_MAKER_CONSUMER_BOOTSTRAP_SERVERS: "kafka-b:9092"
      MIRROR_MAKER_CONSUMER_GROUP_ID: "mm-group-1"
      MIRROR_MAKER_PRODUCER_BOOTSTRAP_SERVERS: "kafka-b:9092"
    command: /usr/bin/start-mirror-maker

4. 数据同步策略选择

数据类型 同步方案 同步频率 适用场景
交易数据 实时同步(同步复制) 毫秒级 金融、订单
用户数据 准实时同步 秒级 用户中心
配置数据 实时同步 秒级 配置中心
日志数据 异步同步 分钟级 日志分析

四、流量调度方案

1. DNS智能解析

DNS是最外层的流量调度:

复制代码
用户(北京) → DNS → 机房A(北京节点)
用户(上海) → DNS → 机房B(上海节点)

DNS服务商配置示例(阿里云DNS):

json 复制代码
{
  "DomainName": "example.com",
  "RegionList": [
    {
      "RegionName": "cn-east-1",
      "RegionId": "cn-east-1",
      "Open": true,
      "IspList": ["default"],
      "RecordCount": 2
    }
  ]
}

2. 应用层路由

java 复制代码
public class RegionRouter {
    
    public String route(HttpServletRequest request) {
        // 根据用户IP判断地域
        String ip = getClientIp(request);
        String region = ipToRegion(ip);
        
        // 根据地域选择机房
        if ("east".equals(region)) {
            return "http://dc-east.example.com";
        } else if ("west".equals(region)) {
            return "http://dc-west.example.com";
        }
        
        return "http://dc-primary.example.com";
    }
}

3. 七层负载均衡调度

nginx 复制代码
# Nginx按地域路由
geo $region {
    default       "primary";
    10.0.0.0/8   "east";
    172.16.0.0/12 "east";
    192.168.0.0/16 "west";
}

upstream backend_east {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

upstream backend_west {
    server 192.168.2.10:8080;
    server 192.168.2.11:8080;
}

upstream backend_primary {
    server 192.168.3.10:8080;
    server 192.168.3.11:8080;
}

server {
    listen 80;
    
    location / {
        set $upstream "";
        
        if ($region = "east") {
            set $upstream backend_east;
        }
        if ($region = "west") {
            set $upstream backend_west;
        }
        if ($upstream = "") {
            set $upstream backend_primary;
        }
        
        proxy_pass http://$upstream;
    }
}

五、故障切换方案

1. 健康检查

java 复制代码
public class HealthChecker {
    
    @Scheduled(fixedRate = 5000)
    public void checkHealth() {
        boolean primaryHealthy = checkDatacenter("primary");
        boolean backupHealthy = checkDatacenter("backup");
        
        if (!primaryHealthy && backupHealthy) {
            // 触发故障切换
            failoverService.switchTo("backup");
            alertService.alert("主数据中心故障,已切换到备份中心");
        }
    }
    
    private boolean checkDatacenter(String dc) {
        try {
            HttpResponse response = httpClient.execute(
                new HttpGet("http://" + dc + "/health/check")
            );
            return response.getStatusLine().getStatusCode() == 200;
        } catch (Exception e) {
            return false;
        }
    }
}

2. DNS切换

java 复制代码
public class DnsSwitcher {
    
    public void switchDns(String targetDc) {
        // 更新DNS记录
        AlidnsClient client = new AlidnsClient();
        
        // 暂停原主站点的A记录
        updateDnsRecord("primary.example.com", null, 0);
        
        // 激活备用站点的A记录
        updateDnsRecord("backup.example.com", getBackupIp(), 600);
        
        // 记录切换日志
        log.info("DNS切换完成: {}", targetDc);
    }
}

3. 切换流程

复制代码
1. 检测到主站点不可达
2. 自动/手动触发切换
3. 停止向主站点写入数据
4. 等待主站点数据同步完成
5. 修改DNS解析
6. 恢复服务
7. 通知相关人员
8. 切换完成后进入监控状态

六、常见问题与解决方案

Q1:跨机房数据冲突怎么办?

解决方案:

  • 按地域划分数据写入,避免冲突
  • 使用分布式ID(如雪花算法)避免ID冲突
  • 对于必须双向同步的数据,使用时间戳+版本号解决冲突
  • 关键冲突场景使用分布式锁

Q2:网络抖动导致切换失败?

解决方案:

  • 设置合理的健康检查阈值和重试次数
  • 使用TCP长连接检测,避免误判
  • 配置多级健康检查(进程+端口+应用)

Q3:如何保证数据一致性?

解决方案:

  • 强一致性场景使用同步复制
  • 最终一致性场景使用异步复制+补偿机制
  • 重要数据采用"写入确认"机制

七、总结

多机房容灾是保障业务高可用的终极方案:

  • 架构选型:根据业务需求选择主备、双活或多活
  • 数据同步:根据数据特性选择同步方案
  • 流量调度:DNS + 应用层双重保障
  • 故障切换:自动化健康检查 + 快速切换

实施建议:

  1. 优先实现数据层的多机房部署
  2. 核心系统采用双活架构
  3. 建立完善的监控和告警体系
  4. 定期进行故障切换演练

思考题:你们系统目前的容灾方案是什么?有哪些改进空间?


个人观点,仅供参考

相关推荐
写代码的小阿帆2 小时前
Web工程结构解析:从MVC分层到DDD领域驱动
java·架构·mvc
Code_LT2 小时前
【AIGC】多 Agent 架构 还是 单Agent?Agent Teams vs SubAgent
架构·aigc
2501_933329552 小时前
企业舆情处置技术实践:基于AI的智能监测与申诉系统架构解析
人工智能·分布式·架构·系统架构
架构师沉默6 小时前
为什么国外程序员都写独立博客,而国内都在公众号?
java·后端·架构
小程故事多_806 小时前
破解Agent“半途摆烂”困局,OpenDev凭Harness架构,撕开Code Agents的工程化真相
人工智能·架构·aigc·harness
Coder个人博客7 小时前
06_apollo_third_party子模块整体软件架构深入分析文档
linux·人工智能·架构
Brandon汐8 小时前
LVS+Keepalived 双主架构全规划(LVS→HAProxy→Web)
容器·架构·lvs
Moe4888 小时前
WebSocket :从浏览器 API 到 Spring 握手、Handler 与前端客户端
java·后端·架构
ai产品老杨9 小时前
异构计算时代的安防底座:基于 Docker 的 X86/ARM 双架构 AI 视频管理平台深度解析
arm开发·docker·架构