原文地址:Nginx Mirror 流量複製的心路歷程html
上篇文章寫到 Lua OpenResty的容器化,項目完成了上雲,可是有面臨了幾個新的問題:nginx
爲了保證服務的質量,咱們決定藉助 nginx mirror(流量複製)
來統計服務的冪等狀況,做爲判斷服務是否達到上線標準的一個重要指標。git
ngx_http_mirror_module 模塊(1.13.4)經過建立後臺鏡像子請求來實現原始請求的鏡像。鏡像子請求的響應將被忽略。github
利用 mirror 模塊,業務能夠將線上實時訪問流量拷貝至其餘環境,基於這些流量能夠作版本發佈前的預先驗證,進行流量放大後的壓測。shell
官方案例:segmentfault
upstream mirror_backend { server mirrorHost:mirrorPort; } location / { mirror /mirror; proxy_pass http://backend; } location = /mirror { internal; proxy_pass http://mirror_backend$request_uri; }
mirror後,能夠經過分析兩邊的access.log,來判斷冪等狀況:api
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。lua
Nginx在1.11.0
版本中就提供了內置變量$request_id
,其原理就是生成32位的隨機字符串,雖不能比擬UUID的機率,但32位的隨機字符串的重複機率也是微不足道了,因此通常可視爲UUID來使用便可。
so,咱們能夠在生產機器的nginx的access.log增長$request_id:spa
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份。request_id
進行排序。