1. 程式人生 > >利用ngx_http_mirror_module實現流量映象

利用ngx_http_mirror_module實現流量映象

背景

最近 Nginx 官網釋出了 Nginx 1.13.4,Nginx 1.13.4 中新增了一個 ngx_http_mirror_module 模組。通過 mirror 模組,可實現對原始請求建立後臺映象,映象子請求的輸出會被忽略。

利用這一功能我們就可以將線上實時訪問流量拷貝至其它環境,基於這些流量可以做版本釋出前的預先驗證或者進行流量放大後的壓測等等。

mirror 模組配置

在這之前與這一功能類似的軟體有:TCPCopy等,Nginx 自身支援後使用起來就更加方便了。下面我們來看看具體的如何配置:

編譯安裝 Nginx

mirror 模組在 Nginx 1.13.4中預設是啟用的,如果要禁用這個功能可在編譯時加上 --without-http_mirror_module

引數。

$ apt-get install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl make build-essential
$ wget http://nginx.org/download/nginx-1.13.4.tar.gz
$ tar xzvf nginx-1.13.4.tar.gz
$ cd nginx-1.13.4/
$ ./configure
$ make && make install

mirror 模組配置分為兩部分:源地址和映象地址配置。配置位置可以為 Nginx 配置檔案的 http

, server, location的上下文中,配置示例為:

# original配置
location / {
    mirror /mirror;
    mirror_request_body off;
    proxy_pass http://127.0.0.1:9502;
}

# mirror配置
location /mirror {
    internal;
    proxy_pass http://127.0.0.1:8081$request_uri;
    proxy_set_header X-Original-URI $request_uri;
}

在這個示例中我們配置了兩組後臺伺服器,http://127.0.0.1:9502

指向生產伺服器, http://127.0.0.1:8081指向測試伺服器。使用者訪問生產伺服器的同時,請求會被 Nginx 複製傳送給測試伺服器,需要注意的一點是 mirror 不會輸出 http 返回內容。

original 配置說明

  • location /

指定了源 uri 為 /

  • mirror /mirror

指定映象 uri 為 /mirror

  • mirror_request_body off | on

指定是否映象請求 Body 部分,此選項與proxy_request_bufferingfastcgi_request_bufferingscgi_request_bufferinguwsgi_request_buffering衝突,一旦開啟mirror_request_body為 on,則請求自動快取。

  • proxy_pass

指定上游 Server 的地址。

mirror 配置說明

  • internal

指定此 location 只能被內部的請求呼叫,外部的呼叫請求會返回 Not found (404)。

  • proxy_pass

指定上游 Server 的地址。

  • proxy_set_header

設定映象流量的頭部。

驗證

映象

按照上述配置,搭建了上圖所示的驗證環境,各個模組均部署在本機,由curl發起請求:

$ curl 127.0.0.1

original 和 mirror 均為上游 Server PHP 指令碼,其中 original 返回響應 response to client。 抓包結果如下圖:

Nginx

分析抓包結果,整個請求流程為:

  • curl 向 Nginx 80 埠發起 GET / HTTP 請求。
  • Nginx 將請求轉發至 upstream 9502 埠的 original PHP 指令碼,Nginx 本地埠為 51637。
  • nginx 將請求映象發至 upstream 8081 埠的 mirror PHP 指令碼,Nginx 本地埠為 51638。
  • original 傳送響應 response to client 至 Nginx。
  • nginx 將響應轉發至 curl,curl 將響應展示到終端。
  • mirror將響應傳送至nginx,nginx丟棄。

由此可見,在整個流程中 Nginx 將請求轉發送至 original 和 mirror,然後等待響應。幾乎不會對正常請求造成影響,整個處理過程是完全非同步的。

mirror 模組實現

static ngx_int_t
ngx_http_mirror_handler_internal(ngx_http_request_t *r)
{
    ngx_str_t                   *name;
    ngx_uint_t                   i;
    ngx_http_request_t          *sr;
    ngx_http_mirror_loc_conf_t  *mlcf;

    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);

    name = mlcf->mirror->elts;

    for (i = 0; i < mlcf->mirror->nelts; i++) {
        if (ngx_http_subrequest(r, &name[i], &r->args, &sr, NULL,
                                NGX_HTTP_SUBREQUEST_BACKGROUND)
            != NGX_OK)
        {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        sr->header_only = 1;
        sr->method = r->method;
        sr->method_name = r->method_name;
    }

    return NGX_DECLINED;
}

Nginx 有關 mirror 的程式碼位於檔案 src/http/modules/ngx_http_mirror_module.c 檔案,上述為檔案中的 ngx_http_mirror_handler_internal 函式。在開啟了 mirror 之後此函式會被執行,可見其內部主要通過 ngx_http_subrequest 發起 http 子請求來實現的。

通過程式碼可見,Nginx 支援配置多個 mirror uri,示例為:

location / {
    mirror /mirror;
    mirror /mirror2;
    mirror_request_body off;
    proxy_pass http://127.0.0.1:9502;
}

location /mirror {
    internal;
    proxy_pass http://127.0.0.1:8081$request_uri;
}

location /mirror2 {
    internal;
    proxy_pass http://127.0.0.1:8081$request_uri;
}

參考文件

http://www.google.com
http://blog.csdn.net/xhjcehust/article/details/77093074
http://nginx.org/en/docs/http/ngx_http_mirror_module.html

文章來自微信公眾號:運維之美