nginx_mirror_module —— Nginx 流量镜像

Nginx 流量镜像 (流量镜像/流量拷贝/流量复制)

Nginx自 1.13.4 开始引入 nginx_mirror_module 模块,利用此模块可以将线上实时流量镜像至其他环境,而Nginx最终 会丢弃 mirror 的响应,从而不影响源站请求的响应。

官方对 nginx_mirror_module 描述如下:

The ngx_http_mirror_module module (1.13.4) implements mirroring of an original request by creating background mirror subrequests.  Responses to mirror subrequests are ignored. 

我们可以这样理解nginx镜像,mirror本意是镜子镜像的意思,故可以理解好比一个镜像站点,将所有的请求都收集起来,这个镜像就代表了所有真实有效的原始请求。有了这些原始请求,后续我们可以将请求引流其它环境的服务去中,比如到线上测试环境。这个过程就实现了线上流量复制。

nginx_mirror_module 模块可用于以下几个场景:

  • 验证服务是否正常以及服务的性能;
  • 复制真实流量实现服务验证,不必造数据,不影响线上正常访问;
  • 与灰度发布不同,镜像流量不会影响真实流量;
  • 以真实流量排查线上问题;
  • 重构 —— 重构场景下,用真实流量检验重构系统;

只需做简单的配置就可实现流量镜像,官方配置如下:

同时官方给出明确指示,若请求体被镜像,那么在创建子请求之前会先读取请求体:

Indicates whether the client request body is mirrored. When enabled, the client request body will be read prior to creating mirror subrequests. In this case, unbuffered client request body proxying set by the proxy_request_buffering, fastcgi_request_buffering, scgi_request_buffering, and uwsgi_request_buffering directives will be disabled.

配置:

nginx_mirror_module

Nginx的安装本文不再赘述,可参考梨主博文《三分钟上手Nginx》https://blog.csdn.net/liyan_1949/article/details/105326558。

nginx_mirror_module 并不是在默认模块中。故此,要使用的时候可以进行自定义安装:

  •  下载源码  http://nginx.org/en/download.html

  • 编译安装:

./configure
    --sbin-path=/usr/local/nginx/nginx
    --conf-path=/usr/local/nginx/nginx.conf
    --pid-path=/usr/local/nginx/nginx.pid
    --with-http_ssl_module
    --without-http_limit_req_module
    --without-http_mirror_module
    --with-pcre=../pcre-8.43
    --with-zlib=../zlib-1.2.11
    --add-module=/path/to/ngx_devel_kit
    --add-module=/path/to/lua-nginx-module

make & make install 

  •      应用配置:

upstream backend {
server backend.local:10000;
}

upstream test_backend {
server test.local:20000;
}

server {
server_name proxy.local;
listen 8000;

location / {
mirror /mirror;
proxy_pass http://backend;
}

location = /mirror {
internal;
proxy_pass http://test_backend$request_uri;
}
}

  • mirror 指令制定镜像 uri 为 /mirror

  • location = /mirror 中的 internal 指定此 location 只能被“内部的”请求调用,外部的调用请求会返回 ”Not found” (404)

Nginx镜像的缺点

镜像机制优势明显,但也并非没有缺憾。

当请求到达 Nginx 时,如果 Nginx 开启了流量镜像功能,它就会将请求复制一份,并根据 mirror location 中的配置来处理这份复制的请求。当前我们只是将复制的请求转发到镜像后端。

最关键部分是复制的镜像请求和原始请求是相关联的,若镜像请求没有处理完成,原始请求就会被阻塞。

总结一下,若镜像请求响应缓慢,原始请求就会被阻塞。

故此在开启流量镜像模式做为测试流量时,要认真监控Nginx性能指标,发现请求反馈异常要即时处理。

解决方案

如果你不确定镜像后端是否能够正确处理原始请求,你可以只复制一部分流量到镜像后端,例如 10%。

mirror 指令没有更多的配置项,它只会将所有的请求复制一份,并根据 mirror location 中的配置来处理请求,所以在 mirror 指令中下手是行不通的,我们只能修改 mirror location 中的配置。修改后的配置文件如下:

 1	upstream backend {
 2	    server backend.local:10000;
 3	}
 4	
 5	upstream test_backend {
 6	    server test.local:20000;
 7	}
 8	
 9	split_clients $remote_addr $mirror_backend {
10	    50% test_backend;
11	    *   "";
12	}
13	
14	server {
15	    server_name proxy.local;
16	    listen 8000;
17	
18	    access_log /var/log/nginx/proxy.log;
19	    error_log /var/log/nginx/proxy.error.log info;
20	
21	    location / {
22	        mirror /mirror;
23	        proxy_pass http://backend;
24	    }
25	
26	    location = /mirror {
27	        internal;
28	        if ($mirror_backend = "") {
29	            return 400;
30	        }
31	
32	        proxy_pass http://$mirror_backend$request_uri;
33	    }
34	
35	}

在 mirror location 中,请求会被转发到 $mirror_backend 变量(32 行)定义的后端。$mirror_backend 变量由 split_clients 配置块定义,split_clients 会将左边的变量 $remote_addr(requests remote address)经过 MurmurHash2 算法进行哈希,得出的值如果在前 50%(从 0 到 2147483500),那么 $mirror_backend 的值为 test_backend;如果不在前 50%,那么 $mirror_backend 的值为空字符 ""

这样就实现了只复制部分流量到镜像后端,如果 $mirror_backend 变量的值为空字符串,就不复制流量;其他情况就会将流量到镜像后端。因为镜像请求的错误响应并不会影响原始请求,所以丢弃镜像请求并返回错误响应是很安全的。

这个方法的优点在于可以根据任何变量或变量组合来拆分镜像流量。如果想真正区分用户,那么 remote address 可能不适合作为拆分镜像流量的依据,因为用户可能会更换 IP。这时最好使用用户粘性密钥来拆分镜像流量,例如 API key

比如,如果根据请求中的 apikey 来拆分镜像流量,只需要将 split_client 配置块中的 $remote_addr 改为 $arg_apikey

split_clients $arg_apikey $mirror_backend {
    50% test_backend;
    *   "";
}

现在如果你查询从 1 到 20 这几个 apikey,只有一半(11)的请求会被复制到镜像后端:

$ for i in {1..20};do curl -i "proxy.local:8000/?apikey=${i}" ;done

查看镜像后端的日志:

...
2019/01/13 22:34:34 addr=127.0.0.1:47224 host=test_backend uri="/?apikey=1"
2019/01/13 22:34:34 addr=127.0.0.1:47230 host=test_backend uri="/?apikey=2"
2019/01/13 22:34:34 addr=127.0.0.1:47240 host=test_backend uri="/?apikey=4"
2019/01/13 22:34:34 addr=127.0.0.1:47246 host=test_backend uri="/?apikey=5"
2019/01/13 22:34:34 addr=127.0.0.1:47252 host=test_backend uri="/?apikey=6"
2019/01/13 22:34:34 addr=127.0.0.1:47262 host=test_backend uri="/?apikey=8"
2019/01/13 22:34:34 addr=127.0.0.1:47272 host=test_backend uri="/?apikey=10"
2019/01/13 22:34:34 addr=127.0.0.1:47278 host=test_backend uri="/?apikey=11"
2019/01/13 22:34:34 addr=127.0.0.1:47288 host=test_backend uri="/?apikey=13"
2019/01/13 22:34:34 addr=127.0.0.1:47298 host=test_backend uri="/?apikey=15"
2019/01/13 22:34:34 addr=127.0.0.1:47308 host=test_backend uri="/?apikey=17"
...

这个方法的奇妙之处在于 split_client 对流量的拆分结果是保持恒定的,apikey=1 的请求会一直被复制到镜像后端。

总结

可以利用Nginx 的 nginx_mirror_module 模块实现流量镜像。但当镜像后端出现响应缓慢原始请求也会被阻塞。此时可以通过 split_client 模块复制部分流量解决上述问题。

                                                                                                                                                              本期梨主|兵长

冻梨邦

本文参考资料

Nginx文档

http://nginx.org/en/docs/

http://nginx.org/en/docs/http/ngx_http_mirror_module.html

http://nginx.org/en/docs/beginners_guide.html

http://nginx.org/en/docs/http/ngx_http_core_module.html#location 

http://nginx.org/en/docs/configure.html 

第三方模板 

http://luajit.org/

https://www.nginx.com/resources/wiki/

https://www.nginx.com/resources/wiki/modules/lua/

https://www.nginx.com/resources/wiki/modules/index.html

https://github.com/openresty/lua-nginx-module 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值