1. 为什么缓存故障会成为"性能灾难"的导火索?

想象一家电商平台正在举行"618"大促活动,用户搜索商品的频率是平时的50倍。此时缓存服务器突然宕机,所有请求瞬间涌入数据库——这就像春运期间高铁站闸机突然全部罢工,人群瞬间把检票员淹没。OpenResty的出现,就像给车站装上了智能应急通道系统,可以在3毫秒内自动切换路线,确保人流依然有序流动。

我们来看一个典型场景:

location /product {
    # 尝试从本地共享字典获取缓存
    local cache = ngx.shared.product_cache:get("p123")
    if cache then
        return ngx.say(cache)
    end
    
    # 缓存未命中则查询Redis集群
    local red = redis:new()
    local res, err = red:get("p123")
    if res then
        ngx.shared.product_cache:set("p123", res, 60) -- 本地缓存60秒
        return ngx.say(res)
    end
    
    # Redis不可用时触发降级逻辑
    return fetch_from_db() -- 最后的数据库防线
}

(技术栈:OpenResty + Redis,此示例展示多级缓存架构)


2. OpenResty的故障应对策略

2.1 主备切换策略(如同备用发电机组)
-- 配置Redis主从节点列表
local servers = {
    {host = "cache1.prod", port = 6379},
    {host = "cache2.prod", port = 6380},
    {host = "cache-backup.prod", port = 6381}
}

local function connect_redis()
    for i, srv in ipairs(servers) do
        local red = redis:new()
        red:set_timeout(500) -- 500毫秒超时
        local ok, err = red:connect(srv.host, srv.port)
        if ok then
            red:set_keepalive(60000, 100) -- 连接池配置
            return red
        end
        ngx.log(ngx.WARN, "Redis节点["..srv.host.."]连接失败: "..err)
    end
    return nil, "所有Redis节点不可用"
end

-- 在请求处理中使用连接器
local red, err = connect_redis()
if not red then
    -- 触发降级流程(后文详解)
end

(技术栈:OpenResty + Redis主从集群,示例展示自动故障切换)

2.2 请求降级策略(类似飞机的迫降程序)

当连续10次连接Redis失败时,自动开启熔断模式:

local circuit_breaker = {
    state = "closed",    -- closed | half-open | open
    failure_count = 0,
    last_failure_time = 0
}

local function handle_request()
    if circuit_breaker.state == "open" then
        if ngx.now() - circuit_breaker.last_failure_time > 60 then
            circuit_breaker.state = "half-open" -- 尝试恢复
        else
            return fallback_to_db() -- 直接跳过失效缓存层
        end
    end

    local ok, res = pcall(connect_redis)
    if not ok then
        circuit_breaker.failure_count = circuit_breaker.failure_count + 1
        if circuit_breaker.failure_count >= 10 then
            circuit_breaker.state = "open"
            circuit_breaker.last_failure_time = ngx.now()
        end
    end
end

(该示例实现了熔断器模式,防止雪崩效应)


3. 健康检查机制:给缓存服务器装"心电图仪"

OpenResty通过lua-resty-upstream-healthcheck模块实现动态健康监测:

# nginx.conf配置
upstream redis_cluster {
    server 192.168.1.10:6379 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:6379 backup;
}

lua_shared_dict healthcheck 1m;

init_worker_by_lua_block {
    local hc = require "resty.upstream.healthcheck"
    local ok, err = hc.spawn_checker{
        shm = "healthcheck",
        upstream = "redis_cluster",
        type = "http",
        http_req = "PING\r\n",  -- Redis协议的特殊处理
        interval = 2000,  -- 2秒检测一次
        timeout = 1000,   -- 1秒超时
        fall = 3,         -- 连续失败3次标记为不可用
        rise = 2          -- 连续成功2次恢复
    }
}

(该配置实现自动节点状态切换,带Redis协议适配)


4. 技术方案的"双刃剑"特性

优势分析:
  • 微秒级切换速度(传统Nginx的10倍以上)
  • 支持动态策略调整(无需重启服务)
  • 精细化的流量控制(可针对不同业务设置不同降级策略)
需要注意的"雷区":
  1. 共享字典溢出风险:
-- 防止本地缓存击穿
local function safe_get(key)
    local val = ngx.shared.cache:get(key)
    if val then return val end
    
    -- 使用锁机制防止缓存穿透
    local lock = ngx.shared.locks:add(key.."_lock", true, 5)
    if lock then
        -- 数据库查询逻辑
        ngx.shared.cache:set(key, new_val)
        ngx.shared.locks:delete(key.."_lock")
    else
        ngx.sleep(0.1)
        return safe_get(key)  -- 递归重试
    end
end
  1. 监控指标盲区补充方案:
# 通过Prometheus采集关键指标
curl http://openresty/metrics | grep redis_health

示例输出:

redis_upstream_health{node="192.168.1.10:6379"} 1
redis_request_latency_ms{method="GET"} 35

5. 真实战场经验:某社交平台故障复盘

故障场景:Redis集群主节点突发网络隔离
处理过程

  1. OpenResty在120ms内检测到连接超时
  2. 自动将流量切换至上海灾备中心
  3. 触发限流策略(QPS从5万降至1万)
  4. 通过灰度发布逐步恢复服务

事后优化的健康检查配置:

hc.spawn_checker{
    ...
    check_interval = 500,  -- 更密集的检测
    req_headers = {"X-HealthCheck: true"},
    valid_statuses = {200, 301},
    concurrency = 5  -- 并行检测
}

6. 总结与最佳实践

核心要义

  • 故障检测速度要快于业务超时时间
  • 降级策略需要分层次(完全降级、部分降级)
  • 状态切换需具备自愈能力

推荐部署架构

客户端 → OpenResty边缘节点 → L1缓存(内存) 
                           → L2缓存(Redis集群) 
                           → 降级服务(限流/静态数据) 
                           → DB(最后的防线)