Nginx Mirror 流量複製的心路歷程

原文地址:Nginx Mirror 流量複製的心路歷程html

背景

上篇文章寫到 Lua OpenResty的容器化,項目完成了上雲,可是有面臨了幾個新的問題:nginx

  1. 舊業務跑了這麼多年,如何保證上雲後的穩定性呢?
  2. 如何確保接口都能正常訪問呢?
  3. 如何確保兩邊api請求的冪等呢?

爲了保證服務的質量,咱們決定藉助 nginx mirror(流量複製)來統計服務的冪等狀況,做爲判斷服務是否達到上線標準的一個重要指標。git

nginx mirror是什麼?

ngx_http_mirror_module 模塊(1.13.4)經過建立後臺鏡像子請求來實現原始請求的鏡像。鏡像子請求的響應將被忽略。github

利用 mirror 模塊,業務能夠將線上實時訪問流量拷貝至其餘環境,基於這些流量能夠作版本發佈前的預先驗證,進行流量放大後的壓測。shell

image.png

官方案例: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後的結果是否冪等?

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

可是要判斷同一個請求在兩邊的結果是否冪等,單目前的信息仍是不夠的,好比:服務器

  1. 如何判斷兩邊是同一個請求?
  2. 如何判斷請求返回的結果是一致的?
  3. 如何對全部請求的冪等性進行統計?

如何判斷兩邊是同一個請求?

要判斷兩邊請求是否同一個,咱們須要一個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: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. 最後經過比較的方式,對兩個文件進行統計。
相關文章
相關標籤/搜索