原文地址:Nginx Mirror 流量复制的心路历程

背景

上篇文章写到 Lua OpenResty的容器化,我的项目实现了上云,然而有面临了几个新的问题:

  1. 旧业务跑了这么多年,如何保障上云后的稳定性呢?
  2. 如何确保接口都能失常拜访呢?
  3. 如何确保两边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

然而要判断同一个申请在两边的后果是否幂等,单目前的信息还是不够的,比方:

  1. 如何判断两边是同一个申请?
  2. 如何判断申请返回的后果是统一的?
  3. 如何对所有申请的幂等性进行统计?

如何判断两边是同一个申请?

要判断两边申请是否同一个,咱们须要一个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:nqrbyg9l50jwu3ezmvtfc6hx487i2kporequest_id0vy27c35aqrsxgzflk4inwtjd1omu96presp_bodymd5后的后果。

如何对所有申请的幂等性进行统计?

至此,咱们曾经失去了咱们想要的日志格局了,通过request_idresp_body能够对两个申请的信息幂等判断。

然而又有一个新的问题,一台机器采集到的access_log就靠近3000W,因为$request_id是字符随机串,所以须要对两边access log进行排序,能力做统计,然而要怎么做呢?

  1. 因为日志太大,所以依据request_id第一个字符进行mod 10,将文件切割成10份。
  2. 对10份log依据request_id进行排序。
  3. 合并文件
  4. 最初通过比拟的形式,对两个文件进行统计。