一、为什么需要请求镜像?

想象这样一个场景:线上用户反馈某个功能异常,但你在测试环境死活复现不出来。这时候如果能"录下"生产环境的真实请求,在测试环境"重放",问题不就迎刃而解了吗?这就是请求镜像要解决的核心问题。

传统做法是查日志、看监控,但往往像盲人摸象:

  • 日志可能缺失关键参数
  • 监控只能看到指标变化
  • 测试数据与真实流量存在差异

我们需要的是一面"镜子",能原原本本反射真实流量。这就是今天要介绍的OpenResty方案。

二、OpenResty的独特优势

OpenResty不是简单的Nginx加Lua,它更像瑞士军刀:

# OpenResty配置示例
location / {
    access_by_lua_block {
        -- 在这里可以同时处理请求转发和镜像
        ngx.log(ngx.INFO, "请求URI: ", ngx.var.request_uri)
    }
}

相比传统方案,它有三大绝活:

  1. 性能无损:基于Nginx的事件驱动模型,镜像操作几乎不增加延迟
  2. 灵活编程:Lua脚本可以精细控制每个请求的镜像逻辑
  3. 协议支持:完美兼容HTTP/1.x,对HTTP/2和WebSocket也有良好支持

三、完整实现方案

3.1 基础镜像配置

# OpenResty配置(生产环境)
server {
    listen 8080;
    
    location /api {
        # 主服务配置
        proxy_pass http://production_backend;
        
        # 镜像配置
        mirror /mirror;
        mirror_request_body on;  # 关键!必须开启body转发
    }

    location = /mirror {
        internal;  # 禁止外部直接访问
        proxy_pass http://test_backend$request_uri;
        proxy_pass_request_body on;
        proxy_set_header X-Mirrored "true";  # 添加标记头
    }
}

这个配置实现了:

  • 所有/api请求会同时发给生产后端
  • 完全相同的请求会静默转发到测试环境
  • 通过X-Mirrored头区分镜像流量

3.2 带条件过滤的进阶版

# OpenResty Lua脚本
location /api {
    access_by_lua_block {
        -- 只镜像特定条件的请求
        if ngx.var.arg_userType == "vip" then
            ngx.req.set_header("X-Mirror-Target", "test_backend")
        end
    }
    
    mirror /mirror;
    mirror_request_body on;
}

location = /mirror {
    internal;
    proxy_pass http://$http_x_mirror_target$request_uri;
}

这个版本新增:

  • 根据userType参数动态控制镜像
  • 可扩展为基于UA、IP等条件的过滤
  • 支持多目标灵活切换

四、实战中的注意事项

4.1 性能调优建议

  1. 连接池配置
proxy_mirror连接需要单独配置连接池
proxy_http_version 1.1;
proxy_set_header Connection "";
keepalive 16;  # 根据实际情况调整
  1. 流量控制
-- 使用令牌桶控制镜像速率
local limit_req = require "resty.limit.req"
local limiter = limit_req.new("my_limit_store", 10, 5)  -- 10req/s + 5突发
local delay, err = limiter:incoming(ngx.var.binary_remote_addr, true)

4.2 常见问题排查

  • Body丢失:检查mirror_request_body是否开启
  • 头信息不全:用curl -v对比原始请求和镜像请求
  • 性能下降:检查mirror location是否出现阻塞操作

五、与其他方案对比

方案 优点 缺点
OpenResty镜像 实时性强,配置灵活 需要维护Lua脚本
日志回放 无需改造生产环境 可能丢失关键上下文
流量录制工具 功能完整 通常需要额外资源部署

特别提醒:对于金融类敏感业务,记得做好数据脱敏:

-- 敏感字段脱敏示例
local sensitive_fields = {"password", "idCard"}
for _, field in ipairs(sensitive_fields) do
    if ngx.var.request_body:find(field) then
        ngx.req.set_body_data(ngx.req.get_body_data():gsub(field .. "=[^&]*", field .. "=****"))
    end
end

六、总结与展望

经过实践验证,这套方案帮助我们:

  • 将生产问题复现时间从平均4小时缩短到15分钟
  • 发现过3次测试环境未能覆盖的边界case
  • 在618大促期间实现流量压测的真实模拟

未来可以结合Service Mesh技术,实现更细粒度的流量控制。但核心思想不变:用最小代价获取最大价值的真实流量。