找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4700

积分

0

好友

645

主题
发表于 2 小时前 | 查看: 2| 回复: 0

适用场景:互联网业务需要跨地域高可用、RPO≈0、RTO<30秒、支撑千万级日活用户的场景。
环境要求:Linux Kernel 4.18+、支持跨地域专线或高速公网链路、数据中心间RTT<50ms(双活)或<100ms(多活)、至少2个独立可用区。


1️⃣ 实施步骤

架构与数据流说明

系统架构:

用户请求 → DNS智能解析(根据地理位置)
    ├─ 华北用户 → 北京机房(主)
    │             ↓
    │        应用集群 + Redis集群 + MySQL主库
    │             ↓
    │        实时数据同步(Canal + MQ)
    │             ↓
    └─ 华南用户 → 深圳机房(主)
                  ↓
             应用集群 + Redis集群 + MySQL主库
                  ↓
             实时数据同步 → 北京机房

关键组件:

  • DNS智能解析:根据用户地理位置就近接入(如DNSPod、阿里云DNS)
  • 应用层无状态:支持跨机房快速漂移,Session存储在Redis
  • 数据同步层:Canal实时捕获MySQL binlog,通过Kafka跨机房传输
  • 数据库架构:MySQL多主或主从+读写分离,Redis集群跨机房部署
  • 流量调度:Nginx/LVS实现机房间流量切换,支持手动和自动故障转移

数据流向:

  1. 用户通过DNS解析到最近机房的LVS VIP(如北京:10.1.1.100,深圳:10.2.1.100)
  2. 请求经过Nginx/应用服务器处理,读本地Redis缓存
  3. 写操作写入本地MySQL主库,通过Canal实时同步binlog到Kafka
  4. Kafka消费者在对端机房消费binlog,异步写入对端MySQL(准实时同步,延迟<3秒)
  5. 北京机房故障时,DNS自动切换到深圳机房,RTO<30秒

Step 1: 网络与基础设施准备

目标: 确保跨机房网络互通、带宽充足、延迟满足要求

环境信息(示例):

  • 北京机房(IDC-BJ)
    • 公网IP段:123.45.67.0/24
    • 内网IP段:10.1.0.0/16
    • LVS VIP:10.1.1.100
    • 出口带宽:10Gbps
    • 专线带宽:2Gbps(到深圳机房)
  • 深圳机房(IDC-SZ)
    • 公网IP段:234.56.78.0/24
    • 内网IP段:10.2.0.0/16
    • LVS VIP:10.2.1.100
    • 出口带宽:10Gbps
    • 专线带宽:2Gbps(到北京机房)

RHEL/CentOS 命令:

# 1. 测试跨机房网络延迟(要求 RTT < 50ms 双活,< 100ms 多活)
ping -c 10 10.2.1.100  # 从北京机房 ping 深圳机房 VIP
# 预期输出:
# 10 packets transmitted, 10 received, 0% packet loss
# rtt min/avg/max/mdev = 42.123/45.678/50.234/2.345 ms

# 2. 测试带宽和丢包率(使用 iperf3)
# 在深圳机房启动 iperf3 服务端
iperf3 -s -p 5201

# 在北京机房测试带宽(10秒测试)
iperf3 -c 10.2.1.100 -p 5201 -t 10 -P 10
# 预期输出:
# [ ID] Interval           Transfer     Bitrate         Retr
# [SUM]   0.00-10.00  sec  2.35 GBytes  2.02 Gbits/sec    0  sender
# [SUM]   0.00-10.00  sec  2.34 GBytes  2.01 Gbits/sec         receiver

# 3. 配置专线路由(优先走专线,公网作为备份)
# 查看当前路由表
ip route show
# 添加静态路由(深圳机房内网走专线)
ip route add 10.2.0.0/16 via 172.16.100.1 dev eth1 metric 10
# 备份路由(公网)
ip route add 10.2.0.0/16 via 123.45.67.1 dev eth0 metric 20

# 4. 持久化路由配置
cat > /etc/sysconfig/network-scripts/route-eth1 <<EOF
10.2.0.0/16 via 172.16.100.1 dev eth1 metric 10
EOF

# 5. 检查防火墙规则(放行跨机房通信端口)
# MySQL复制:3306, Redis集群:6379-6389, Kafka:9092, Canal:11111
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.2.0.0/16" accept'
firewall-cmd --reload

Ubuntu/Debian 命令:

# 1-2步同 RHEL/CentOS

# 3. 配置专线路由
ip route add 10.2.0.0/16 via 172.16.100.1 dev ens192 metric 10
ip route add 10.2.0.0/16 via 123.45.67.1 dev ens160 metric 20

# 4. 持久化路由(使用 netplan,Ubuntu 18.04+)
cat >> /etc/netplan/01-netcfg.yaml <<EOF
    routes:
      - to: 10.2.0.0/16
        via: 172.16.100.1
        metric: 10
      - to: 10.2.0.0/16
        via: 123.45.67.1
        metric: 20
EOF
netplan apply

# 5. 防火墙配置(使用 ufw)
ufw allow from 10.2.0.0/16
ufw reload

关键参数解释:

  1. metric 10/20:路由优先级,数值越小优先级越高。10=专线(高优先级),20=公网(备份)
  2. iperf3 -P 10:并发10个流测试,模拟真实并发场景,避免单流受TCP窗口限制
  3. firewall-cmd --add-rich-rule:富规则允许整个对端机房IP段访问,简化管理

执行前验证:

# 确认专线链路状态
ip link show eth1
# 预期输出:state UP(链路正常)

# 确认对端机房防火墙未阻断
telnet 10.2.1.100 3306
# 预期输出:Connected to 10.2.1.100(能建立连接)

执行后验证:

# 验证路由优先级
ip route get 10.2.1.100
# 预期输出:10.2.1.100 via 172.16.100.1 dev eth1 src 10.1.1.10(走专线)

# 验证丢包率(长时间监控)
mtr -r -c 100 10.2.1.100
# 预期输出:Loss% < 0.5%,Avg < 50ms

常见错误与处理:

# 错误1:RTT 过高(>100ms)
# 原因:可能走了公网而非专线
# 解决:traceroute 10.2.1.100,检查路由路径,调整 metric 优先级

# 错误2:带宽测试远低于预期
# 原因:TCP窗口过小、MTU不匹配
# 解决:调大TCP窗口(net.ipv4.tcp_window_scaling=1)、检查MTU(ip link show | grep mtu)

Step 2: MySQL 跨机房主主复制部署

目标: 实现双机房MySQL准实时同步,写入延迟<3秒,支持双向复制

环境信息:

  • 北京MySQL主库:10.1.2.10:3306(server-id=1)
  • 深圳MySQL主库:10.2.2.10:3306(server-id=2)
  • 数据库版本:MySQL 8.0.32(要求8.0+支持GTID)

RHEL/CentOS 命令:

# === 在北京机房MySQL主库(10.1.2.10)执行 ===

# 1. 安装MySQL 8.0(使用官方Yum仓库)
yum install -y https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm
yum install -y mysql-community-server

# 2. 配置MySQL主库参数
cat > /etc/my.cnf.d/replication.cnf <<EOF
[mysqld]
# 服务器标识(全局唯一)
server-id = 1

# 启用二进制日志(GTID模式)
log-bin = mysql-bin
binlog_format = ROW
gtid_mode = ON
enforce_gtid_consistency = ON

# 主从复制配置
log-slave-updates = 1
relay-log = relay-bin
relay-log-index = relay-bin.index

# 性能优化
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1

# 跨机房优化(减少网络往返)
slave_compressed_protocol = 1
slave_net_timeout = 60

# 并行复制(加速同步)
slave_parallel_workers = 4
slave_parallel_type = LOGICAL_CLOCK
EOF

# 3. 启动MySQL并设置开机自启
systemctl enable --now mysqld

# 4. 获取临时root密码并初始化
grep 'temporary password' /var/log/mysqld.log
# 输出:[Note] A temporary password is generated for root@localhost: Abcd1234!

# 登录并修改密码
mysql -uroot -p'Abcd1234!' -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewRootPass@2024';"

# 5. 创建复制账号(允许深圳机房访问)
mysql -uroot -p'NewRootPass@2024' <<EOF
CREATE USER 'repl'@'10.2.%' IDENTIFIED WITH mysql_native_password BY 'ReplPass@2024';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.2.%';
FLUSH PRIVILEGES;
EOF

# 6. 查看Master状态(记录GTID位置)
mysql -uroot -p'NewRootPass@2024' -e "SHOW MASTER STATUS\G"
# 预期输出:
#              File: mysql-bin.000001
#          Position: 156
#      Binlog_Do_DB:
#  Binlog_Ignore_DB:
# Executed_Gtid_Set: e8f9a6b3-1234-5678-9abc-def012345678:1-5

# === 在深圳机房MySQL主库(10.2.2.10)执行类似操作 ===
# 修改 server-id = 2,其余配置相同

# 7. 配置深圳到北京的复制通道
mysql -uroot -p'NewRootPass@2024' <<EOF
CHANGE MASTER TO
  MASTER_HOST='10.1.2.10',
  MASTER_PORT=3306,
  MASTER_USER='repl',
  MASTER_PASSWORD='ReplPass@2024',
  MASTER_AUTO_POSITION=1,
  MASTER_CONNECT_RETRY=10,
  MASTER_RETRY_COUNT=86400,
  MASTER_COMPRESSION_ALGORITHMS='zstd';
START SLAVE;
EOF

# 8. 在北京机房配置反向复制(北京到深圳)
mysql -uroot -p'NewRootPass@2024' <<EOF
CHANGE MASTER TO
  MASTER_HOST='10.2.2.10',
  MASTER_PORT=3306,
  MASTER_USER='repl',
  MASTER_PASSWORD='ReplPass@2024',
  MASTER_AUTO_POSITION=1,
  MASTER_CONNECT_RETRY=10,
  MASTER_RETRY_COUNT=86400,
  MASTER_COMPRESSION_ALGORITHMS='zstd';
START SLAVE;
EOF

# 9. 验证复制状态(两端都执行)
mysql -uroot -p'NewRootPass@2024' -e "SHOW SLAVE STATUS\G" | grep -E 'Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master'
# 预期输出:
# Slave_IO_Running: Yes
# Slave_SQL_Running: Yes
# Seconds_Behind_Master: 0

关键参数解释:

  1. gtid_mode=ON:基于GTID的复制,自动跟踪事务位置,故障切换时无需手动指定binlog位点
  2. slave_compressed_protocol=1:启用压缩传输,跨机房场景可减少50%+带宽消耗
  3. slave_parallel_workers=4:4个并行工作线程应用binlog,加速同步(根据CPU核数调整)

执行后验证:

# 测试双向写入(避免写入冲突)
# 北京机房:写入user表ID为奇数的记录
mysql -uroot -p'NewRootPass@2024' -e "INSERT INTO test.user (id, name) VALUES (1, 'Beijing-User1');"

# 深圳机房:写入user表ID为偶数的记录
mysql -uroot -p'NewRootPass@2024' -e "INSERT INTO test.user (id, name) VALUES (2, 'Shenzhen-User2');"

# 验证数据同步(等待3秒后查询)
sleep 3
mysql -uroot -p'NewRootPass@2024' -e "SELECT * FROM test.user;" # 两端都应能看到2条记录

常见错误与处理:

# 错误1:Slave_IO_Running: Connecting(无法连接Master)
# 原因:网络不通或防火墙阻断
# 解决:telnet 10.1.2.10 3306,检查网络和防火墙

# 错误2:Slave_SQL_Running: No,错误:Error 'Duplicate entry'
# 原因:主键冲突(双主场景常见)
# 解决:跳过该错误事务(仅开发环境):
#       SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;
# 生产解决:设计时避免冲突(如北京用奇数ID,深圳用偶数ID)

幂等性保障:

  • 使用GTID自动跳过重复事务
  • 重复执行START SLAVE不会产生副作用

Step 3: Redis 跨机房集群部署

目标: 部署Redis集群,实现跨机房缓存同步,故障切换<5秒

环境信息:

  • 北京Redis集群:10.1.3.11-16(3主3从,端口7000-7005)
  • 深圳Redis集群:10.2.3.11-16(3主3从,端口7000-7005)
  • Redis版本:Redis 7.0.12(要求7.0+支持跨机房Active-Active)

RHEL/CentOS 命令:

# 1. 安装Redis 7.0(使用Remi仓库)
yum install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm
yum module enable redis:remi-7.0 -y
yum install -y redis

# 2. 创建集群配置目录(以7000端口为例)
mkdir -p /data/redis/{7000,7001,7002}/{data,logs}

# 3. 生成集群节点配置文件(自动化脚本)
for port in {7000..7002}; do
cat > /data/redis/$port/redis.conf <<EOF
# 基础配置
bind 0.0.0.0
port $port
daemonize yes
pidfile /var/run/redis_$port.pid
logfile /data/redis/$port/logs/redis.log
dir /data/redis/$port/data

# 集群配置
cluster-enabled yes
cluster-config-file nodes-$port.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 0

# 持久化
appendonly yes
appendfsync everysec

# 内存优化
maxmemory 4gb
maxmemory-policy allkeys-lru

# 安全
requirepass Redis@2024
masterauth Redis@2024
EOF
done

# 4. 启动所有Redis实例
for port in {7000..7002}; do
    redis-server /data/redis/$port/redis.conf
done

# 5. 验证进程启动
ps aux | grep redis-server
# 预期输出:6个进程(北京3个Master + 深圳3个Slave)

# 6. 创建Redis集群(仅在一个节点执行)
# 安装 redis-cli 集群工具
redis-cli --cluster create \
  10.1.3.11:7000 10.1.3.12:7001 10.1.3.13:7002 \
  10.1.3.14:7000 10.1.3.15:7001 10.1.3.16:7002 \
  --cluster-replicas 1 \
  -a Redis@2024
# 预期输出:[OK] All 16384 slots covered.

# 7. 验证集群状态
redis-cli -c -h 10.1.3.11 -p 7000 -a Redis@2024 CLUSTER INFO
# 预期输出:
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_known_nodes:6

跨机房同步配置(使用RedisShake):

# 8. 部署RedisShake实现跨机房同步
# 下载RedisShake(二进制部署)
wget https://github.com/tair-opensource/RedisShake/releases/download/v3.1.0/redis-shake-linux-amd64.tar.gz
tar -xzf redis-shake-linux-amd64.tar.gz -C /usr/local/bin/

# 9. 配置RedisShake(北京到深圳单向同步)
cat > /etc/redis-shake/sync.toml <<EOF
[source]
type = "cluster"
address = "10.1.3.11:7000,10.1.3.12:7001,10.1.3.13:7002"
password = "Redis@2024"

[target]
type = "cluster"
address = "10.2.3.11:7000,10.2.3.12:7001,10.2.3.13:7002"
password = "Redis@2024"

[advanced]
rdb_restore_command_behavior = "rewrite"
target_redis_client_max_querybuf_len = 1073741824
target_redis_proto_max_bulk_len = 536870912
EOF

# 10. 启动RedisShake
nohup redis-shake sync.toml > /var/log/redis-shake.log 2>&1 &

# 11. 验证同步延迟
redis-cli -c -h 10.1.3.11 -p 7000 -a Redis@2024 SET testkey "Beijing-$(date +%s)"
sleep 1
redis-cli -c -h 10.2.3.11 -p 7000 -a Redis@2024 GET testkey
# 预期输出:Beijing-1705320123(延迟<1秒)

关键参数解释:

  1. cluster-replicas 1:每个Master配置1个Slave,保证高可用
  2. cluster-node-timeout 15000:节点超时15秒判定故障(跨机房建议>15s)
  3. RedisShake:阿里云开源工具,支持Redis集群间实时同步,延迟<1秒

执行后验证:

# 测试故障切换(停止一个Master节点)
redis-cli -h 10.1.3.11 -p 7000 -a Redis@2024 DEBUG SLEEP 30

# 查看集群自动Failover
redis-cli -c -h 10.1.3.12 -p 7001 -a Redis@2024 CLUSTER NODES | grep master
# 预期:原Slave已提升为Master

Step 4: 应用层无状态改造与Session共享

目标: 应用服务器无状态部署,Session存储在Redis集群,支持跨机房漂移

Java应用改造(Spring Boot示例):

# 1. 添加Spring Session Redis依赖(pom.xml)
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
# 2. 配置Session存储到Redis(application.yml)
spring:
session:
store-type: redis
redis:
flush-mode: on_save
namespace: spring:session
redis:
cluster:
nodes:
- 10.1.3.11:7000
- 10.1.3.12:7001
- 10.1.3.13:7002
max-redirects: 3
password: Redis@2024
lettuce:
pool:
max-active: 200
max-idle: 50
min-idle: 10
# 3. 启用Session共享注解(Application.java)
@SpringBootApplication
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class Application {
public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

验证Session跨机房共享:

# 4. 部署应用到两个机房
# 北京机房:10.1.4.11-13(3个应用节点)
# 深圳机房:10.2.4.11-13(3个应用节点)

# 5. 测试Session一致性
# 步骤1:访问北京机房应用并登录
curl -i -c cookies.txt http://10.1.4.11:8080/login -d "username=test&password=test123"
# 预期:Set-Cookie: SESSION=abc123...

# 步骤2:使用相同Cookie访问深圳机房应用
curl -i -b cookies.txt http://10.2.4.11:8080/user/profile
# 预期:返回用户信息(Session共享成功)

Step 5: DNS智能解析与流量调度

目标: 配置DNS智能解析,实现用户就近接入和故障自动切换

使用DNSPod配置智能解析:

# 1. 添加A记录(双线路)
# 主机记录:www
# 记录类型:A
# 线路类型:默认
# 记录值:123.45.67.100(北京机房LVS VIP)
# TTL:600(10分钟)

# 2. 添加分地域解析
# 线路类型:华北
# 记录值:123.45.67.100(北京机房)

# 线路类型:华南
# 记录值:234.56.78.100(深圳机房)

# 3. 配置健康检查(DNSPod付费功能)
# 监控URL:http://123.45.67.100/health
# 检查频率:30秒
# 超时时间:5秒
# 失败切换:连续2次失败则切换到备用线路

自建DNS健康检查脚本(开源方案):

#!/bin/bash
# /usr/local/bin/dns_failover.sh
# 用途:监控机房健康状态,故障时自动切换DNS

API_ID="your_dnspod_api_id"
API_TOKEN="your_dnspod_api_token"
DOMAIN="example.com"
RECORD_ID="1234567" # DNS记录ID

BJ_VIP="123.45.67.100"
SZ_VIP="234.56.78.100"
CURRENT_IP=$(dig +short www.example.com @8.8.8.8 | head -1)

# 健康检查
check_health() {
local ip=$1
    curl -sf --max-time 3 http://$ip/health >/dev/null 2>&1
return $?
}

# 切换DNS记录
update_dns() {
local new_ip=$1
    curl -X POST "https://dnsapi.cn/Record.Modify" \
        -d "login_token=$API_ID,$API_TOKEN" \
        -d "domain=$DOMAIN" \
        -d "record_id=$RECORD_ID" \
        -d "sub_domain=www" \
        -d "record_type=A" \
        -d "record_line=默认" \
        -d "value=$new_ip" \
        -d "ttl=600"
}

# 主逻辑
if ! check_health "$BJ_VIP"; then
echo "[WARN] 北京机房故障,切换到深圳机房"
if [ "$CURRENT_IP" != "$SZ_VIP" ]; then
        update_dns "$SZ_VIP"
echo "[INFO] DNS已切换到 $SZ_VIP"
fi
elif ! check_health "$SZ_VIP"; then
echo "[WARN] 深圳机房故障,切换到北京机房"
if [ "$CURRENT_IP" != "$BJ_VIP" ]; then
        update_dns "$BJ_VIP"
echo "[INFO] DNS已切换到 $BJ_VIP"
fi
fi

Crontab定时执行:

# 每分钟检查一次
* * * * * /usr/local/bin/dns_failover.sh >> /var/log/dns_failover.log 2>&1

2️⃣ 最小必要原理

核心机制:

异地多活架构通过数据分区就近路由实现跨地域高可用。用户请求通过DNS智能解析就近接入,每个机房独立提供完整服务能力(计算+存储),机房间通过准实时数据同步(MySQL GTID复制、Redis跨机房同步、Kafka消息队列)保持数据最终一致性。这种架构的核心挑战在于如何在保障性能的同时,处理好分布式系统的一致性问题。

数据一致性策略:

  1. 强一致性数据(如订单、支付):单向写入主机房,通过Binlog同步到备机房(延迟<3秒)
  2. 最终一致性数据(如用户信息、商品信息):允许双向写入,通过冲突解决策略(Last-Write-Wins)保证最终一致
  3. 缓存数据:Redis跨机房异步同步,允许短暂不一致(<1秒)

故障切换流程:

  1. 健康检查检测到主机房故障(连续3次HTTP请求失败)
  2. DNS自动切换或手动切换流量到备机房(TTL=600秒,10分钟内全部生效)
  3. 备机房接管全部流量,从本地MySQL/Redis读取数据
  4. RTO(恢复时间目标):DNS切换10分钟 + 客户端缓存过期5分钟 = 15分钟(优化后可降至30秒)

为什么RPO≈0?

  • MySQL使用GTID+半同步复制,主库提交事务前至少1个从库确认写入binlog
  • 关键业务数据(订单/支付)写入主机房后立即同步到备机房
  • 故障发生时,最多丢失最后3秒的写入数据

3️⃣ 可观测性

监控指标

Linux原生监控:

# 1. 监控跨机房网络延迟
ping -c 10 10.2.1.100 | tail -1
# 预期输出:rtt min/avg/max/mdev = 42.1/45.6/50.2/2.3 ms

# 2. 监控MySQL主从延迟
mysql -uroot -p'NewRootPass@2024' -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master
# 预期输出:Seconds_Behind_Master: 2(延迟2秒,正常)

# 3. 监控Redis集群状态
redis-cli -c -h 10.1.3.11 -p 7000 -a Redis@2024 CLUSTER INFO | grep cluster_state
# 预期输出:cluster_state:ok

# 4. 监控Kafka消费延迟(如使用Kafka同步数据)
/opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server 10.1.5.11:9092 \
  --describe --group canal-consumer
# 预期输出:LAG < 1000(消息堆积<1000条)

Prometheus告警规则:

groups:
- name: multi_region_alerts
  interval: 30s
  rules:
  # 告警1:跨机房网络延迟过高
  - alert: CrossRegionHighLatency
    expr: probe_duration_seconds{job="blackbox-ping"} > 0.1
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "跨机房延迟过高({{ $value }}秒 > 100ms)"
      description: "机房间网络延迟异常,可能影响数据同步"

  # 告警2:MySQL主从延迟过高
  - alert: MySQLReplicationLag
    expr: mysql_slave_status_seconds_behind_master > 10
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "MySQL主从延迟过高({{ $value }}秒)"
      description: "数据同步延迟过高,可能导致数据不一致"

  # 告警3:Redis集群不可用
  - alert: RedisClusterDown
    expr: redis_cluster_state != 1
    for: 30s
    labels:
      severity: critical
    annotations:
      summary: "Redis集群状态异常({{ $labels.instance }})"

  # 告警4:机房健康检查失败
  - alert: DatacenterHealthCheckFailed
    expr: probe_success{job="http-health"} == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "机房健康检查失败({{ $labels.datacenter }})"
      action: "立即执行DNS切换流程"

性能基准测试:

# 场景1:跨机房数据同步延迟测试
# 北京机房写入
mysql -h10.1.2.10 -uroot -p -e "INSERT INTO test.benchmark (created_at) VALUES (NOW());"
# 记录时间戳,深圳机房查询到该记录的时间差
# 预期:延迟 < 3秒

# 场景2:故障切换RTO测试
# 步骤1:停止北京机房LVS
systemctl stop keepalived
# 步骤2:监控DNS切换时间和业务恢复时间
# 预期:RTO < 30秒(DNS TTL=10秒 + 健康检查=10秒 + 客户端重试=10秒)

# 场景3:跨机房带宽压测
iperf3 -c 10.2.1.100 -t 60 -P 20
# 预期:2Gbps专线带宽利用率 > 80%

4️⃣ 常见故障与排错

故障编号 症状 诊断命令 可能根因 快速修复 永久修复
#1 MySQL主从延迟突增(>30秒) SHOW SLAVE STATUS\G | grep Seconds_Behind 1. 大事务阻塞
2. 网络带宽打满
3. 从库IO瓶颈
跳过大事务(慎用)
STOP SLAVE; START SLAVE;
1. 拆分大事务
2. 扩容专线带宽
3. 升级从库磁盘
#2 Redis跨机房同步中断 ps aux | grep redis-shake 1. RedisShake进程挂掉
2. 网络闪断
3. Redis集群故障切换
重启RedisShake
systemctl restart redis-shake
1. 配置进程守护(systemd)
2. 增加重试机制
3. 监控进程状态
#3 DNS切换后部分用户仍访问故障机房 dig www.example.com 1. 客户端DNS缓存未过期
2. 运营商LocalDNS缓存
3. TTL设置过长
等待DNS TTL过期(10分钟) 1. 缩短TTL至60秒
2. 提前公告维护
3. 使用HttpDNS
#4 Session丢失(用户被登出) redis-cli GET spring:session:... 1. Redis集群脑裂
2. Session未同步到对端机房
3. 应用配置错误
用户重新登录 1. 检查Redis集群状态
2. 验证RedisShake同步
3. 检查应用配置
#5 跨机房网络延迟突增 mtr -r 10.2.1.100 1. 专线故障切换到公网
2. 运营商网络抖动
3. 路由表错误
检查路由:ip route get 10.2.1.100 1. 联系运营商检查专线
2. 配置专线SLA告警
3. 修正路由配置

详细排查案例:

案例1:MySQL主从延迟突增排查

# Step 1:确认延迟来源
mysql -e "SHOW SLAVE STATUS\G" | grep -E 'Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running'
# 如果 Slave_SQL_Running: Yes 但延迟高 → SQL线程慢(从库性能瓶颈)
# 如果 Slave_IO_Running: Connecting → 网络问题

# Step 2:查看从库正在执行的事务
mysql -e "SHOW PROCESSLIST;" | grep "system user"
# 查找长时间Running的SQL

# Step 3:查看Binlog事件大小
mysql -e "SHOW BINLOG EVENTS IN 'mysql-bin.000123' LIMIT 10;"
# 查找大事务(如单条INSERT百万行)

# 永久解决:
# 1. 拆分大事务(应用层改造)
# 2. 启用并行复制(已配置slave_parallel_workers=4)
# 3. 监控慢查询:pt-query-digest /var/log/mysql/slow.log

5️⃣ 变更与回滚剧本

灰度策略

# 场景:升级深圳机房应用版本

# Step 1:先在深圳机房单台节点灰度(10.2.4.11)
# 从LVS摘除该节点
ipvsadm -d -t 10.2.1.100:80 -r 10.2.4.11:8080

# 部署新版本
scp app-v2.0.jar 10.2.4.11:/opt/app/
ssh 10.2.4.11 "systemctl restart app"

# 验证新版本(模拟10%流量)
for i in {1..100}; do curl -s http://10.2.4.11:8080/health; done

# Step 2:观察30分钟无异常后,逐步扩大范围
# 灰度到深圳机房全部节点 → 北京机房单台 → 北京机房全部

回滚条件与命令

回滚触发条件:

  1. 错误率(5xx)超过1%
  2. 响应时间P99超过基线2倍
  3. 关键业务指标下降超过10%(如下单量)

回滚步骤:

# 1. 快速回滚应用版本(使用备份包)
ssh 10.2.4.11 "cp /opt/app/app-v1.9.jar.bak /opt/app/app-v1.9.jar && systemctl restart app"

# 2. 回滚数据库变更(使用备份SQL)
mysql -h10.2.2.10 -uroot -p < /opt/backup/rollback_20240115.sql

# 3. 回滚Redis配置
redis-cli -h 10.2.3.11 -p 7000 -a Redis@2024 --eval /opt/backup/redis_rollback.lua

# 4. 验证回滚成功
curl http://10.2.4.11:8080/version
# 预期输出:v1.9

6️⃣ 最佳实践

  1. 数据分区避免冲突
    • 北京机房处理用户ID为奇数的请求
    • 深圳机房处理用户ID为偶数的请求
    • 或按地域分区(华北用户→北京,华南用户→深圳)
  2. 缩短DNS TTL
    # 平时设置为10分钟(减轻DNS服务器压力)
    # 变更前12小时缩短至60秒(加快故障切换)
  3. 定期演练故障切换(每季度一次)
    # 演练脚本
    #!/bin/bash
    echo "开始故障切换演练:$(date)"
    # 1. 停止北京机房LVS
    ssh 10.1.1.10 "systemctl stop keepalived"
    # 2. 监控DNS切换和业务恢复时间
    # 3. 30分钟后恢复
    ssh 10.1.1.10 "systemctl start keepalived"
  4. 使用单元化架构
    • 将业务拆分为独立单元(如按城市、按业务线)
    • 每个单元独立部署,减少跨单元依赖
    • 单元故障不影响其他单元
  5. 监控关键业务指标
    • 除了技术指标(延迟、错误率),还需监控业务指标(下单量、支付成功率)
    • 业务指标异常时自动触发回滚

7️⃣ FAQ

Q1: 异地多活和异地灾备的区别?
A: 异地多活是多个机房同时对外提供服务,流量分散到各机房;异地灾备是备机房平时不提供服务,仅故障时切换。多活成本更高但RTO更短(<30秒 vs 30分钟)。

Q2: 如何选择双活还是多活?
A:

  • 双活:2个机房,适合跨区域业务(如华北+华南),成本较低,延迟<50ms
  • 多活:3个及以上机房,适合全国或全球业务,成本更高,需要更复杂的流量调度

Q3: 数据冲突如何解决?
A:

  1. 避免冲突:设计时通过数据分区避免双写(推荐)
  2. 冲突解决:使用Last-Write-Wins(最后写入获胜)或Vector Clock(向量时钟)
  3. 人工介入:关键数据冲突时触发告警,人工确认

Q4: 如何降低跨机房同步延迟?
A:

  1. 使用专线替代公网(延迟从100ms降至<10ms)
  2. 启用MySQL并行复制(slave_parallel_workers=4)
  3. 启用压缩传输(slave_compressed_protocol=1)
  4. 优化网络参数(TCP窗口、MTU)

Q5: DNS切换为什么需要10分钟?
A: DNS TTL(缓存时间)默认10分钟,客户端和LocalDNS在TTL过期前不会重新查询。可缩短至60秒,但会增加DNS服务器压力。生产环境建议使用HttpDNS(APP直连DNS服务器,绕过运营商LocalDNS)。

Q6: 如何验证异地多活架构的有效性?
A: 定期进行混沌工程演练

  1. 随机停止一个机房的部分节点
  2. 模拟网络分区(使用iptables阻断跨机房通信)
  3. 模拟数据库故障(停止MySQL主库)
  4. 验证RTO/RPO是否满足要求

8️⃣ 附录:关键脚本

脚本1:跨机房健康检查与自动切换

#!/bin/bash
##############################################################################
# 文件名:multi_region_health_check.sh
# 版本:v1.0.0
# 用途:监控跨机房健康状态,自动切换DNS
# 说明:支持钉钉/企业微信告警、Prometheus指标导出
##############################################################################

set -euo pipefail

# 配置参数
BJ_VIP="123.45.67.100"
SZ_VIP="234.56.78.100"
HEALTH_CHECK_URL="/health"
CHECK_TIMEOUT=3
FAILURE_THRESHOLD=3
ALERT_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=xxx"

# 状态文件
STATE_FILE="/var/run/health_check_state"
ALERT_COOLDOWN=300  # 告警冷却时间5分钟

# 初始化状态
if [[ ! -f $STATE_FILE ]]; then
echo "BJ_FAIL_COUNT=0" > $STATE_FILE
echo "SZ_FAIL_COUNT=0" >> $STATE_FILE
echo "LAST_ALERT=0" >> $STATE_FILE
fi

source $STATE_FILE

# 健康检查函数
check_health() {
local ip=$1
local url="http://${ip}${HEALTH_CHECK_URL}"

    http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time $CHECK_TIMEOUT "$url")

if [[ "$http_code" == "200" ]]; then
return 0
else
echo "[WARN] 健康检查失败: $url (HTTP $http_code)"
return 1
fi
}

# 告警函数
send_alert() {
local message=$1
local current_time=$(date +%s)

# 冷却期内不重复告警
if (( current_time - LAST_ALERT < ALERT_COOLDOWN )); then
return
fi

    curl -s -X POST "$ALERT_WEBHOOK" \
        -H 'Content-Type: application/json' \
        -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"【异地多活告警】$message\"}}"

echo "LAST_ALERT=$current_time" >> $STATE_FILE
}

# DNS切换函数(调用DNSPod API)
switch_dns() {
local target_ip=$1
echo "[INFO] 切换DNS到 $target_ip"
# 这里调用DNSPod API或阿里云DNS API
# 示例:curl -X POST "https://dnsapi.cn/Record.Modify" ...
}

# 主逻辑
echo "=== 健康检查开始:$(date) ==="

# 检查北京机房
if check_health "$BJ_VIP"; then
echo "[OK] 北京机房健康"
    BJ_FAIL_COUNT=0
else
    BJ_FAIL_COUNT=$((BJ_FAIL_COUNT + 1))
echo "[FAIL] 北京机房故障次数:$BJ_FAIL_COUNT"

if (( BJ_FAIL_COUNT >= FAILURE_THRESHOLD )); then
        send_alert "北京机房连续${FAILURE_THRESHOLD}次健康检查失败,已切换到深圳机房"
        switch_dns "$SZ_VIP"
fi
fi

# 检查深圳机房
if check_health "$SZ_VIP"; then
echo "[OK] 深圳机房健康"
    SZ_FAIL_COUNT=0
else
    SZ_FAIL_COUNT=$((SZ_FAIL_COUNT + 1))
echo "[FAIL] 深圳机房故障次数:$SZ_FAIL_COUNT"

if (( SZ_FAIL_COUNT >= FAILURE_THRESHOLD )); then
        send_alert "深圳机房连续${FAILURE_THRESHOLD}次健康检查失败,已切换到北京机房"
        switch_dns "$BJ_VIP"
fi
fi

# 保存状态
cat > $STATE_FILE <<EOF
BJ_FAIL_COUNT=$BJ_FAIL_COUNT
SZ_FAIL_COUNT=$SZ_FAIL_COUNT
LAST_ALERT=$LAST_ALERT
EOF

# 导出Prometheus指标
cat > /var/lib/node_exporter/textfile_collector/health_check.prom <<EOF
# HELP datacenter_health_status Datacenter health check status (1=healthy, 0=failed)
# TYPE datacenter_health_status gauge
datacenter_health_status{datacenter="beijing"} $([[ $BJ_FAIL_COUNT -eq 0 ]] && echo 1 || echo 0)
datacenter_health_status{datacenter="shenzhen"} $([[ $SZ_FAIL_COUNT -eq 0 ]] && echo 1 || echo 0)
EOF

echo "=== 健康检查完成 ==="

Crontab配置:

# 每30秒检查一次
* * * * * /usr/local/bin/multi_region_health_check.sh
* * * * * sleep 30; /usr/local/bin/multi_region_health_check.sh

脚本2:MySQL跨机房延迟监控

#!/bin/bash
# 文件名:mysql_replication_lag_monitor.sh
# 用途:监控MySQL主从延迟,延迟过高时告警

MYSQL_USER="monitor"
MYSQL_PASS="MonitorPass@2024"
MYSQL_HOST="10.1.2.10"
THRESHOLD=10  # 延迟阈值(秒)

LAG=$(mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -Nse "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master | awk '{print $2}')

if [[ "$LAG" == "NULL" ]]; then
echo "ERROR: 主从复制异常(Seconds_Behind_Master为NULL)"
exit 1
fi

echo "MySQL主从延迟: ${LAG}秒"

if (( LAG > THRESHOLD )); then
echo "WARNING: 延迟超过阈值(${LAG}秒 > ${THRESHOLD}秒)"
# 发送告警
fi

# 导出Prometheus指标
echo "mysql_slave_status_seconds_behind_master{host=\"$MYSQL_HOST\"} $LAG" > \
    /var/lib/node_exporter/textfile_collector/mysql_lag.prom

希望这份详尽的异地多活架构实战指南能帮助你构建稳固的跨机房容灾体系。在实际操作中,建议先在测试环境充分验证,再逐步灰度上线。如果你在搭建过程中遇到任何问题,欢迎来 云栈社区 的运维或架构板块与同行们交流探讨。




上一篇:Kali Linux 2025.2 VMware虚拟机安装与网络报错换源解决攻略
下一篇:AI记忆系统MemPalace开源:生化危机女主打造,获长记忆基准测试满分
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-8 08:58 , Processed in 0.750277 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表