Nginx 流量镜像配置文档(10% 比例采样)
适用文件:
- http.conf
Nginx/OpenResty 版本:支持
split_clients与mirror指令的版本(OpenResty ≥ 1.13.x / Nginx ≥ 1.13.x)。 部署形态:两份配置互为副本,结构相同,通常部署在不同节点或环境,共用同一套镜像策略。
一、概述
在http.conf 中加入了一套**轻量级、可控比例的流量镜像(Traffic Mirroring / Shadowing)**机制:
-
目标 :把生产线上经过 Nginx 的请求,按一定比例复制一份到镜像后端,用于影子流量分析、压测、问题复现等。
-
核心原则:
-
镜像请求不影响主链路业务(响应不返回给客户端)。
-
通过
split_clients做 A/B 比例控制 ,本次设置为 10% 开启 / 90% 关闭。 -
镜像后端通过
internal内部 location 转发,对外不可直接访问。 -
镜像链路设置严格的超时(连接 500ms、收发 3s),避免拖垮主链路。
-
二、详解
改动 1:在 server 块开头新增 split_clients
位置 :server 块内、location 之前,请求方法校验之后。
if ( $request_method !~* GET|POST|OPTIONS ) {
return 405;
}
# 流量镜像比例控制:10%
split_clients $request_id $mirror_ratio {
10% mirror_enable;
* mirror_disable;
}
作用:
-
使用 nginx 的
split_clients指令,对每一个请求 (以$request_id作为 hash 输入)做稳定 hash 抽样:-
10% 的请求
$mirror_ratio = mirror_enable(允许镜像)。 -
90% 的请求
$mirror_ratio = mirror_disable(禁止镜像)。
-
-
由于
split_clients基于 hash,同一请求 ID 的采样结果稳定,便于灰度分析。
2:在 internal_mirror_sn 中增加条件判断
# 镜像流量内部 location
location = /internal_mirror_sn {
internal;
proxy_pass http://mirror_sn$request_uri;
if ($mirror_ratio = mirror_disable) {
return 404;
}
# 同步透传请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Mirror-Flag "sn-traffic";
...
}
作用:
-
借助 nginx
mirror指令的"跳转到 internal location"机制,先到达/internal_mirror_sn; -
在真正发起后端转发前,二次校验
$mirror_ratio:-
如果是
mirror_disable(即 90% 的请求),直接return 404,立刻丢弃镜像请求,不再占用上游连接; -
只有
mirror_enable时才执行proxy_pass真正把请求打到镜像后端。
-
nginx
mirror指令本身不支持 做条件判断(它是无条件触发),所以必须借由internal location + if实现"按比例开关"。
三、整体流量走向与控制原理
1. 关键组件
| 组件 | 说明 |
|---|---|
split_clients $request_id $mirror_ratio |
比例控制器,10/90 分流 |
mirror /internal_mirror_sn |
镜像指令,在每个 location 中触发 |
location = /internal_mirror_sn(internal) |
镜像接收与转发点 |
upstream mirror_sn |
镜像后端集群(当前指向 国产化环境的SLB) |
proxy_next_upstream off |
镜像失败不重试,避免阻塞主链路 |
2. 单请求完整流向图
┌─────────────────────────────────────────┐
客户端请求 ──▶ │ Nginx ( 生产环境节点 server 块) │
│ │
│ 1) 方法校验 (GET/POST/OPTIONS 通过) │
│ 2) split_clients 按 $request_id 抽样 │
│ → 10% → mirror_enable │
│ → 90% → mirror_disable │
│ 3) 匹配对应 location,正常 proxy_pass │
│ 到业务上游(主链路,不受影响) │
│ 4) 同时触发 mirror 指令 │
└───────────────┬─────────────────────────┘
│ (镜像)
▼
┌─────────────────────────────────────────┐
│ location = /internal_mirror_sn │
│ (internal,仅供内部跳转) │
│ │
│ if ($mirror_ratio = mirror_disable) │
│ return 404; ← 丢弃(90% 请求) │
│ else │
│ proxy_pass http://mirror_sn$uri │
│ → 10.248.135.95:20080 │
└─────────────────────────────────────────┘
│
▼
镜像后端(用于影子分析)
3. 主链路 vs 镜像链路(关键区别)
| 维度 | 主链路 | 镜像链路 |
|---|---|---|
| 是否返回给客户端 | ✅ 是 | ❌ 否(响应丢弃) |
| 失败是否影响业务 | 是 | 否(连接失败只记日志) |
| 超时设置 | 通常 60s/180s | 严格 500ms/3s |
| 是否允许重试 | 通常 proxy_next_upstream on |
off(禁止重试) |
| 是否带请求体 | 根据业务而定 | mirror_request_body on(带 body) |
| 采样比例 | 100% | 10% |
四、哪些流量走镜像,哪些不走?
1. 走镜像的 location(已显式声明 mirror /internal_mirror_sn)
通过扫描两份配置,以下 location 均启用了镜像:
| 业务模块 | Location 前缀/精确路径 | 说明 |
|---|---|---|
| 工作流图 | /workflow-diagram |
工作流图代理 |
| 数字化监管 | /szhjg/、/szhjg-rmp/ |
数字化监管相关接口 |
| PXF 代理 | /pxf-proxy- |
HTTPS 上游代理 |
| 网关 | /px-gateway |
网关入口 |
| 登录 | /login |
登录接口 |
| K8s API | /k8s-api/ |
/k8s-api/api/xxx → /api/xxx |
| 通用网关 | /px-、/sx-、/fbs-、/sn- |
微服务网关调用 |
| FDD | /pxf-common-fdd |
大文件上传 |
| WebSocket | /websocket、/noticeWebsocket、/chanelmoitorWebsocket、/mpushWebsocket、/nettyWebsocket |
WebSocket 入口 |
| Nacos | /nacos |
注册中心 UI/API |
| AI/聊天 | /chat、/knowlwdge_base、/ai- |
智能问答相关 |
以上 location 内 10% 的请求会被镜像,90% 会在
internal入口被丢弃。
2. 不走镜像的请求
-
所有非 GET/POST/OPTIONS 方法(如
PUT、DELETE、PATCH):在 server 入口被return 405直接拒绝,不会进入任何 location,自然也不会被镜像。 -
静态资源类 location (未配置
mirror):这些 location 直接返回本地文件,不发起镜像:-
/error_page/ -
/ -
/.well-known/pki-validation/ -
/ht-portal/ -
/pxf-k8s-tool/、/res/ -
/pxf-(注意:这是静态资源分支,与/px-网关分支不同) -
/common/ -
/index.html、/pxf-(*)/index.html
-
-
特殊 rewrite 重定向 :
location = /pxf-sxservice-omarketservice/#/1(rewrite ^ https://www.baidu.com/)--- 直接重定向,不进入 mirror 链路。
3. 总结一句话
凡是配置了
mirror /internal_mirror_sn的 location ,其请求中的 10% 会被同步复制一份发往镜像后端;未配置 mirror 的 location 与 被 405 拦截的请求 永远不会走镜像。
五、延时分析
镜像链路设计了硬性短超时以保护主链路:
| 阶段 | 配置 | 超时值 |
|---|---|---|
| 建立连接 | proxy_connect_timeout |
500 ms |
| 读取响应 | proxy_read_timeout |
3 s |
| 发送请求 | proxy_send_timeout |
3 s |
| 重试 | proxy_next_upstream off |
失败不重试 |
| Keepalive | keepalive 64、keepalive_timeout 30s、keepalive_requests 500 |
长连接复用 |
对主链路的影响
-
nginx
mirror指令默认行为是异步 ,镜像请求与主请求并行发送,主链路响应时间不会直接叠加镜像耗时。 -
唯一会影响主链路的场景 :镜像后端连接耗尽(极端情况),在
internal中排队等待,但连接超时被限制在 500 ms 内。 -
由于
mirror_request_body on,POST 等带 body 的请求会先把 body 落盘/缓存再镜像,body 较大时会有少量 I/O 开销。
实际经验值
| 场景 | 镜像对主链路延时的额外影响 |
|---|---|
| 镜像后端正常、空闲 | < 1 ms(异步 + keepalive) |
| 镜像后端慢 / 超时 | 主链路几乎无影响(500 ms 触发超时后丢弃) |
| 大 body 镜像(>1 MB) | 1~5 ms(body 缓存) |
| 镜像后端宕机 | 主链路无影响(连接 500 ms 内失败并丢弃) |
六、上游(upstream)配置说明
upstream mirror_sn {
server 国产化环境地址和端口;
keepalive 64;
keepalive_timeout 30s;
keepalive_requests 500;
}
-
如需高可用可扩展多台:
upstream mirror_sn { server 1.1.1.1:80; server 1.1.1.2:80; keepalive 64; keepalive_timeout 30s; keepalive_requests 500; } -
keepalive 64:每个 worker 与上游最多维持 64 条长连接,复用连接降低握手开销。 -
keepalive_requests 500:单条长连接最多处理 500 个请求后主动重建,避免长时间复用带来的资源泄漏。 -
keepalive_timeout 30s:空闲连接保持 30 秒。
七、镜像请求的关键 Header
/internal_mirror_sn 中透传了以下 header,便于后端识别与审计:
| Header | 值 | 用途 |
|---|---|---|
Host |
$host |
保留原始域名 |
X-Real-IP |
$remote_addr |
真实客户端 IP |
X-Forwarded-For |
$proxy_add_x_forwarded_for |
完整代理链路 |
X-Mirror-Flag |
sn-traffic |
镜像流量标识,后端可据此区分影子流量与真实流量 |
Connection |
"" |
关闭客户端侧的 Connection 头,配合 keepalive |
镜像后端服务必须识别
X-Mirror-Flag: sn-traffic,避免将影子流量写入生产库 / 发送通知 / 触发副作用。
八、比例控制:如何调整采样率?
只需修改 split_clients 块即可(两文件同步修改):
# 关闭镜像(全量丢弃)
split_clients $request_id $mirror_ratio {
0% mirror_enable;
* mirror_disable;
}
# 50% 镜像
split_clients $request_id $mirror_ratio {
50% mirror_enable;
* mirror_disable;
}
# 100% 全量镜像
split_clients $request_id $mirror_ratio {
* mirror_enable;
}
调整后 nginx -s reload 即可热生效(无需重启进程)。
九、部署与验证步骤
1. 语法检查
nginx -t -c nginx.conf
2. 热加载
nginx -s reload
3. 验证镜像是否生效
方法一:观察镜像后端 access log
-
在 国产化服务上检查请求量是否符合 ~10% 比例。
-
抽样日志中的
X-Mirror-Flag: sn-trafficheader 是否存在。
方法二:观察 nginx 自带的 mirror log
tail -f /var/logs/nginx/mirror_sn_access.log
tail -f /var/logs/nginx/mirror_sn_error.log
方法三:临时改为 100% 做冒烟测试
split_clients $request_id $mirror_ratio {
* mirror_enable; # 临时全量
}
验证通过后改回 10%。
4. 确认 90% 请求被正确丢弃
-
抽样
mirror_sn_error.log,可见 404(被if丢弃的请求不会产生这条日志,nginx 会直接 short-circuit)。 -
主链路响应时间不应出现明显异常波动。
十、故障排查清单
| 现象 | 可能原因 | 处理建议 |
|---|---|---|
| 镜像后端收到 0 请求 | split_clients 写成 0%,或 internal 的 if 误判 |
检查 $mirror_ratio 实际值,开启 debug log |
| 主链路变慢 | 镜像后端连接耗尽 / body 太大 | 调小采样率、扩大 keepalive、限制 body 大小 |
| 镜像后端写入生产库 | 后端未识别 X-Mirror-Flag |
后端增加拦截逻辑:识别到 X-Mirror-Flag 时跳过副作用 |
| 比例不准(差几个百分点) | hash 抖动 + 业务低谷 | 正常现象,split_clients 是统计意义上的近似 |
| reload 后配置未生效 | nginx 不支持 split_clients |
升级到 OpenResty ≥ 1.13.x 或 Nginx ≥ 1.13.x |
十一、附录:完整配置文件(参考)
A. split_clients 块
# 流量镜像比例控制:10%
split_clients $request_id $mirror_ratio {
10% mirror_enable;
* mirror_disable;
}
B. if 判断以及对应location下开启流量镜像
if ($mirror_ratio = mirror_disable) {
return 404;
}
#在每个对应的location下开启
mirror /internal_mirror_sn;
mirror_request_body on;
C. 完整的镜像接收 location(参考现状)
location = /internal_mirror_sn {
internal;
proxy_pass http://mirror_sn$request_uri;
if ($mirror_ratio = mirror_disable) {
return 404;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Mirror-Flag "sn-traffic";
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 500ms;
proxy_read_timeout 3s;
proxy_send_timeout 3s;
proxy_next_upstream off;
}