原文地址:Nginx Mirror 流量复制的心路历程
背景
上篇文章写到 Lua OpenResty 的容器化,我的项目实现了上云,然而有面临了几个新的问题:
- 旧业务跑了这么多年,如何保障上云后的稳定性呢?
- 如何确保接口都能失常拜访呢?
- 如何确保两边 api 申请的幂等呢?
为了保障服务的品质,咱们决定借助 nginx mirror(流量复制)
来统计服务的幂等状况,作为判断服务是否达到上线规范的一个重要指标。
nginx mirror 是什么?
ngx_http_mirror_module 模块(1.13.4)通过创立后盾镜像子申请来实现原始申请的镜像。镜像子申请的响应将被疏忽。
利用 mirror 模块,业务能够将线上实时拜访流量拷贝至其余环境,基于这些流量能够做版本公布前的事后验证,进行流量放大后的压测。
官网案例:
upstream mirror_backend {server mirrorHost:mirrorPort;} location / { mirror /mirror; proxy_pass http://backend; } location = /mirror { internal; proxy_pass http://mirror_backend$request_uri; }
如何判断 mirror 后的后果是否幂等?
mirror 后,能够通过剖析两边的 access.log,来判断幂等状况:
170.21.40.147 [20/Apr/2021:14:02:02 +0000] "POST /test/query?client_name=test&client_version=2148205812&client_sequence=1&r=1618327396996&isvip=0&release_version=1.1.0
然而要判断同一个申请在两边的后果是否幂等,单目前的信息还是不够的,比方:
- 如何判断两边是同一个申请?
- 如何判断申请返回的后果是统一的?
- 如何对所有申请的幂等性进行统计?
如何判断两边是同一个申请?
要判断两边申请是否同一个,咱们须要一个 Unique Tracing ID,并且贯通两边的 access.log。
Nginx 在
1.11.0
版本中就提供了内置变量$request_id
,其原理就是生成 32 位的随机字符串,虽不能比较 UUID 的概率,但 32 位的随机字符串的反复概率也是微不足道了,所以个别可视为 UUID 来应用即可。
so,咱们能够在生产机器的 nginx 的 access.log 减少 $request_id:
log_format access_log_format
''$remote_addr $request_time $request $request_id';
并将 $request_id
通过 header 和申请一起转发到 mirror 的服务器上:
location = /mirror {
internal;
proxy_set_header X-Request-Id $request_id; # $request_id
proxy_pass http://test_backend$request_uri;
}
最初在 mirror 服务器的 nginx access 日志格局减少:
$http_request_id
,就能够失去:
170.21.40.147 [20/Apr/2021:14:02:02 +0000] "POST /test/query?client_name=test&client_version=2148205812&client_sequence=1&r=1618327396996&isvip=0&release_version=1.1.0 nqrbyg9l50jwu3ezmvtfc6hx487i2kpo
两个通过 nqrbyg9l50jwu3ezmvtfc6hx487i2kpo
就能够将申请关联上了。
如何判断申请返回的后果是统一的?
$request_id
曾经有了,那么如何判断后果是否统一呢?这里能够将两边服务器的返回后果(resp_body
)进行 md5,而后进行比拟。
在两边的 nginx 定义变量:
set $resp_body "";
body_filter_by_lua '
local resp_body = string.sub(ngx.arg[1], 1, 1000)
ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_body
if ngx.arg[2] then
ngx.var.resp_body = ngx.md5(ngx.ctx.buffered)
end
';
而后在 log_format access_log_format
减少$resp_body
,即:
log_format access_log_format
''$remote_addr $request_time $request $request_id $resp_body';
得出日志:
170.21.40.147 [20/Apr/2021:14:02:02 +0000] "POST /test/query?client_name=test&client_version=2148205812&client_sequence=1&r=1618327396996&isvip=0&release_version=1.1.0 nqrbyg9l50jwu3ezmvtfc6hx487i2kpo 0vy27c35aqrsxgzflk4inwtjd1omu96p
ps:nqrbyg9l50jwu3ezmvtfc6hx487i2kpo
为 request_id
,0vy27c35aqrsxgzflk4inwtjd1omu96p
为resp_body
md5 后的后果。
如何对所有申请的幂等性进行统计?
至此,咱们曾经失去了咱们想要的日志格局了,通过 request_id
和resp_body
能够对两个申请的信息幂等判断。
然而又有一个新的问题,一台机器采集到的 access_log 就靠近 3000W
,因为$request_id
是字符随机串,所以须要对两边 access log 进行排序,能力做统计,然而要怎么做呢?
- 因为日志太大,所以依据
request_id
第一个字符进行 mod 10,将文件切割成 10 份。 - 对 10 份 log 依据
request_id
进行排序。 - 合并文件
- 最初通过比拟的形式,对两个文件进行统计。