Skip to content

Memcached 故障检测

故障检测方法

Memcached 故障检测是确保高可用架构的关键组件,主要包括客户端检测、服务端检测、网络层面检测和外部监控系统检测等方法。及时准确的故障检测可以帮助系统快速响应,减少故障对业务的影响。

客户端故障检测

1. 连接超时检测

实现方式

  • 设置合理的连接超时和读写超时
  • 监控连接建立时间和数据传输时间
  • 对超时情况进行计数和告警

Python 示例

python
#!/usr/bin/env python3
import memcache
import time
from collections import defaultdict

# 设置超时参数
mc = memcache.Client(
    ['localhost:11211'],
    connect_timeout=1,  # 连接超时(秒)
    timeout=1,          # 读写超时(秒)
    retry_timeout=3     # 重试超时(秒)
)

# 超时统计
timeout_count = defaultdict(int)

def safe_get(key):
    """安全获取数据,统计超时情况"""
    try:
        start_time = time.time()
        value = mc.get(key)
        end_time = time.time()
        
        # 记录响应时间
        response_time = end_time - start_time
        if response_time > 0.5:  # 超过 500ms 记录为慢响应
            timeout_count['slow_response'] += 1
        
        return value
    except memcache.MemcachedTimeoutError:
        timeout_count['timeout'] += 1
        return None
    except Exception as e:
        timeout_count['error'] += 1
        return None

# 使用示例
for i in range(100):
    value = safe_get(f'test_key_{i}')

print(f"超时统计: {dict(timeout_count)}")

2. 心跳检测

实现方式

  • 定期发送简单命令(如 statsget 一个已知键)
  • 检测响应是否正常
  • 对无响应节点进行标记和处理

Java 示例

java
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.AddrUtil;

public class HeartbeatExample {
    private static MemcachedClient client;
    private static final String HEARTBEAT_KEY = "__heartbeat__";
    
    public static void main(String[] args) throws Exception {
        // 初始化客户端
        client = new MemcachedClient(AddrUtil.getAddresses("localhost:11211"));
        
        // 初始化心跳键
        client.set(HEARTBEAT_KEY, 0, "1");
        
        // 定期执行心跳检测
        while (true) {
            boolean isAlive = checkHeartbeat();
            System.out.println("Memcached 状态: " + (isAlive ? "正常" : "异常"));
            Thread.sleep(5000);  // 每 5 秒检测一次
        }
    }
    
    private static boolean checkHeartbeat() {
        try {
            Object result = client.get(HEARTBEAT_KEY);
            return result != null;
        } catch (Exception e) {
            return false;
        }
    }
}

3. 故障自动切换

实现方式

  • 维护可用节点列表
  • 对故障节点进行自动隔离
  • 实现故障转移机制
  • 定期尝试恢复故障节点

Python 示例

python
#!/usr/bin/env python3
import memcache
import time

class MemcachedFailoverClient:
    def __init__(self, servers):
        self.servers = servers
        self.available_servers = servers.copy()
        self.failed_servers = []
        self.retry_interval = 30  # 30秒后重试故障节点
        self.last_retry_time = time.time()
        
    def get_client(self):
        """获取可用的客户端连接"""
        if not self.available_servers:
            # 尝试恢复所有故障节点
            self.try_recover_servers()
            if not self.available_servers:
                raise Exception("所有 Memcached 服务器不可用")
        
        return memcache.Client(self.available_servers)
    
    def try_recover_servers(self):
        """尝试恢复故障节点"""
        current_time = time.time()
        if current_time - self.last_retry_time < self.retry_interval:
            return
        
        self.last_retry_time = current_time
        
        # 尝试恢复每个故障节点
        recovered = []
        for server in self.failed_servers:
            try:
                client = memcache.Client([server])
                client.set("__test__", "1", 5)
                result = client.get("__test__")
                if result == "1":
                    recovered.append(server)
                    print(f"服务器 {server} 已恢复")
            except:
                continue
        
        # 更新服务器列表
        for server in recovered:
            self.failed_servers.remove(server)
            self.available_servers.append(server)
    
    def mark_server_failed(self, server):
        """标记服务器为故障状态"""
        if server in self.available_servers:
            self.available_servers.remove(server)
            self.failed_servers.append(server)
            print(f"服务器 {server} 标记为故障")
    
    def get(self, key):
        """安全获取数据,处理故障转移"""
        try:
            client = self.get_client()
            return client.get(key)
        except Exception as e:
            # 简单实现:假设是第一个服务器故障
            if self.servers:
                self.mark_server_failed(self.servers[0])
            return self.get(key)  # 递归重试

# 使用示例
servers = ['localhost:11211', 'localhost:11212', 'localhost:11213']
client = MemcachedFailoverClient(servers)

# 正常使用
value = client.get('test_key')
print(f"获取值: {value}")

服务端故障检测

1. 内置状态检查

stats 命令检查

bash
# 检查服务状态
telnet localhost 11211
stats

# 检查关键指标
STAT pid 1234
STAT uptime 3600
STAT time 1620000000
STAT version 1.6.18
STAT accepting_conns 1  # 1 表示正在接受连接
STAT listen_disabled_num 0  # 0 表示没有拒绝连接
END

监控接受连接状态

  • accepting_conns:1 表示正常接受连接,0 表示暂停接受连接
  • listen_disabled_num:累计拒绝连接次数,应保持为 0

2. 资源使用监控

CPU 使用率监控

bash
# 查看 Memcached 进程 CPU 使用率
top -p $(pgrep memcached)

# 或使用 pidstat
pidstat -p $(pgrep memcached) 1 5

内存使用率监控

bash
# 查看 Memcached 内存使用
ps aux | grep memcached

# 或使用 smem
smem -p $(pgrep memcached)

磁盘 I/O 监控

bash
# 查看磁盘 I/O 情况
iotop -p $(pgrep memcached)

3. 日志监控

监控错误日志

bash
# 实时监控 Memcached 日志
tail -f /var/log/memcached.log | grep -i "error\|fail\|warn"

# 统计错误次数
grep -i "error\|fail\|warn" /var/log/memcached.log | wc -l

关键错误信息

  • bad data chunk:数据块格式错误
  • invalid command:无效命令
  • out of memory writing item:内存不足
  • error reading from client:读取客户端数据错误

网络层面故障检测

1. 端口连通性检测

使用 telnet 检测

bash
# 检测端口连通性
telnet localhost 11211

# 或使用 nc
nc -z localhost 11211
if [ $? -eq 0 ]; then
    echo "端口 11211 可达"
else
    echo "端口 11211 不可达"
fi

批量检测脚本

bash
#!/bin/bash
# 批量检测 Memcached 服务器连通性

targets=("localhost:11211" "localhost:11212" "localhost:11213")

echo "Memcached 服务器连通性检测结果"
echo "=============================="

for target in "${targets[@]}"; do
    host=$(echo $target | cut -d: -f1)
    port=$(echo $target | cut -d: -f2)
    
    if nc -z -w 2 $host $port; then
        echo "✓ $target - 连通"
    else
        echo "✗ $target - 不通"
    fi
done

2. 网络延迟检测

使用 ping 检测

bash
# 检测网络延迟
ping -c 5 localhost

# 或使用 hping3 进行 TCP ping
hping3 -c 5 -S -p 11211 localhost

使用 mtr 进行路径分析

bash
# 检测网络路径和延迟
mtr localhost

3. 防火墙规则检查

检查本地防火墙规则

bash
# iptables 规则检查
iptables -L -n | grep 11211

# firewalld 规则检查
firewall-cmd --list-ports | grep 11211

# ufw 规则检查
ufw status | grep 11211

外部监控系统检测

1. Prometheus + Grafana

配置 memcached_exporter

bash
# 启动 memcached_exporter
./memcached_exporter --memcached.address=localhost:11211

Prometheus 告警规则

yaml
groups:
- name: memcached-alerts
  rules:
  - alert: MemcachedDown
    expr: memcached_up == 0
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "Memcached 服务不可用"
      description: "实例 {{ $labels.instance }} 的 Memcached 服务已停止响应 5 分钟"
  
  - alert: MemcachedHighConnectionCount
    expr: memcached_current_connections / memcached_max_connections > 0.9
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Memcached 连接数过高"
      description: "实例 {{ $labels.instance }} 的连接数已超过最大连接数的 90% 持续 5 分钟"
  
  - alert: MemcachedHighEvictionRate
    expr: rate(memcached_evictions_total[5m]) > 100
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Memcached 驱逐率过高"
      description: "实例 {{ $labels.instance }} 的驱逐率超过每秒 100 次持续 5 分钟"

2. Zabbix 监控

Zabbix 模板配置

  • 导入 Memcached 监控模板
  • 配置监控项:连接数、内存使用率、缓存命中率等
  • 设置触发器:服务不可达、连接数过高、驱逐率过高等
  • 配置告警媒介:邮件、短信、Slack 等

自定义监控项示例

# 监控 Memcached 状态
UserParameter=memcached.status[*],echo stats | nc $1 $2 | grep -c "STAT"

# 监控连接数
UserParameter=memcached.connections[*],echo stats | nc $1 $2 | grep "STAT curr_connections" | awk '{print $3}'

3. Nagios 监控

Nagios 插件配置

bash
# 安装 check_memcached 插件
yum install nagios-plugins-memcached

# 或编译安装
wget https://github.com/nagios-plugins/nagios-plugins/releases/download/release-2.3.3/nagios-plugins-2.3.3.tar.gz
tar -xzf nagios-plugins-2.3.3.tar.gz
cd nagios-plugins-2.3.3
./configure
make
make install

服务配置示例

define service {
    use                     generic-service
    host_name               memcached-server
    service_description     Memcached Status
    check_command           check_memcached!11211
    notifications_enabled   1
}

define service {
    use                     generic-service
    host_name               memcached-server
    service_description     Memcached Connections
    check_command           check_memcached!11211!-w 800 -c 900
    notifications_enabled   1
}

故障检测最佳实践

1. 多层级检测策略

建议实现

  • 客户端层面:连接超时、心跳检测、故障自动切换
  • 服务端层面:资源监控、日志监控、状态检查
  • 网络层面:端口连通性、网络延迟、防火墙规则
  • 外部系统:集中式监控、告警机制、自动恢复

2. 合理设置检测频率

检测频率建议

  • 客户端心跳:5-30 秒
  • 服务端资源监控:1-5 分钟
  • 网络连通性检测:1-5 分钟
  • 外部系统监控:10-60 秒

3. 告警分级机制

告警级别设置

  • 严重:服务不可用、数据丢失风险
  • 警告:资源使用率高、性能下降
  • 信息:状态变化、配置变更

告警通知渠道

  • 严重告警:短信、电话、即时通讯
  • 警告告警:邮件、即时通讯
  • 信息告警:日志记录

4. 自动化故障处理

自动化处理流程

  1. 故障检测:发现异常状态
  2. 故障确认:多次检测确认故障
  3. 故障隔离:将故障节点从集群中移除
  4. 故障恢复:尝试自动恢复故障节点
  5. 故障通知:发送告警通知
  6. 故障记录:记录故障信息和处理过程

5. 定期演练

故障演练建议

  • 定期进行故障注入测试
  • 验证故障检测机制的有效性
  • 测试故障恢复流程
  • 评估故障对业务的影响
  • 优化故障处理流程

常见问题(FAQ)

Q1: 如何快速检测 Memcached 服务是否正常?

A1: 快速检测方法:

bash
# 使用 telnet 检测
nc -z -w 2 localhost 11211 && echo "服务正常" || echo "服务异常"

# 或使用 memcached-tool
sudo memcached-tool localhost:11211 stats > /dev/null 2>&1 && echo "服务正常" || echo "服务异常"

Q2: 客户端如何处理 Memcached 故障?

A2: 客户端处理方法:

  • 设置合理的超时时间
  • 实现重试机制
  • 维护可用节点列表
  • 实现故障自动切换
  • 定期检测故障节点

Q3: 如何区分网络故障和服务故障?

A3: 区分方法:

  • 网络故障:所有客户端都无法连接,ping 不通,nc 无法连接
  • 服务故障:ping 通,但无法建立连接或执行命令,服务进程可能异常

Q4: 故障检测频率设置多少合适?

A4: 建议:

  • 生产环境:10-30 秒
  • 非生产环境:1-5 分钟
  • 关键业务:5-10 秒
  • 非关键业务:30-60 秒

Q5: 如何减少故障检测的误报?

A5: 减少误报方法:

  • 多次检测确认(如连续 3 次检测失败才判定为故障)
  • 设置合理的超时时间
  • 考虑网络抖动因素
  • 结合多个指标进行判断
  • 定期调整检测阈值

Q6: 如何实现 Memcached 集群的故障自动恢复?

A6: 自动恢复实现:

  • 定期检测故障节点
  • 当故障节点恢复时,自动将其重新加入集群
  • 实现平滑的流量切换
  • 监控恢复后的节点性能

Q7: 故障检测会对 Memcached 性能产生影响吗?

A7: 影响分析:

  • 合理的检测频率对性能影响很小
  • 过于频繁的检测会增加 Memcached 负载
  • 建议使用轻量级的检测方法
  • 考虑在低峰期增加检测频率