Ngnix 流量拷贝

在需要真实的流量做旁路测试的时候,我们就可以使用 nginx 来做流量的拷贝

1 Nginx Mirror 模块

nginx 自1.13.4 版本开始,自带一个 ngx_http_mirror_module,使用起来非常简单,只要在需要 mirror 的 location 处添加 mirror 关键字就行

Copy

1
2
3
4
5
6
7
8
9
location / {
    mirror /mirror;
    proxy_pass http://backend;
}
 
location = /mirror {
    internal;
    proxy_pass http://test_backend$request_uri;
}

 

一个简单的 nginx 配置如下:

Copy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
http {
 
    upstream webserver1 {
        server  127.0.0.1:8881;
    }
 
    upstream webserver2 {
        server  127.0.0.1:8882;
    }
 
    server {
        listen       8080;
        server_name  localhost;
 
        location / {
            root   html;
            index  index.html index.htm;
        }
 
 
    location /test {
            mirror /mirror;
            proxy_pass http://webserver1/index;
        }
 
        location /mirror {
            internal;
            proxy_pass http://webserver2/index;
        }
 
    }
 
}

 

2 lua 脚本分发请求

但是 nginx 版本过低时,该如何做呢?
我们可以使用集成来 lua 的 OpenResty,使用 lua 脚本处理 web 请求完成流量的拷贝
我们直接看例子:
nginx.conf

Copy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
user root;
worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
 
    upstream product {
            server  127.0.0.1:8881;
    }
    upstream test {
            server  127.0.0.1:8882;
    }
    server {
            listen      8080;
            server_name 172.27.133.28;
            #lua_code_cache off;
 
            # 外部应用的请求地址,通过 lua 脚本分发到新的 product 和 test 地址
            location /predict/fake {
                    client_body_buffer_size 2m;
                    set $switch    "on";              #开启或关闭copy功能
                    content_by_lua_file /tmp/copy_req.lua;
            }
             
            # lua 脚本分发到 product,并且对 url 重写后,反向代理到 product 的 server
            location ~* ^/product {
                    log_subrequest on;
                    rewrite ^/product(.*)$ /predict break;
                    proxy_set_header Host $host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_pass http://product;
                    access_log logs/product-upstream.log;
            }
 
            # lua 脚本分发到 test,并且对 url 重写后,反向代理到 test 的 server
            location ~* ^/test {
                    log_subrequest on;
                    rewrite ^/test(.*)$ /predict break;
                    proxy_set_header Host $host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_pass http://test;
                    access_log logs/test-upstream.log;
            }
 
 
    }
 
}

 

copy_req.lua

Copy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
local res1,res2,action
action = ngx.var.request_method
if action == "POST" then
        arry = {method = ngx.HTTP_POST,body = ngx.req.read_body()}
else
        arry = {method = ngx.HTTP_GET}
end
 
if ngx.var.switch == "on" then
        res1,res2 = ngx.location.capture_multi {    -- 分发请求的核心函数
                { "/product" .. ngx.var.request_uri,arry},{ "/test" .. ngx.var.request_uri,}
else
        res1,res2 = ngx.location.capture_multi {
                { "/product" .. ngx.var.request_uri,}
end
 
if res1.status == ngx.HTTP_OK then
        local header_list = {"Content-Length","Content-Type","Content-Encoding","Accept-Ranges"}
        for _,i in ipairs(header_list) do
                if res1.header[i] then
                        ngx.header[i] = res1.header[i]
                end
        end
        ngx.say(res1.body)
else
        ngx.status = ngx.HTTP_NOT_FOUND
end

 

我们来捋一捋这个流程:
背景:首先我们向外部注册的地址的是 /predict/fake,我们实际 web server 的地址是 /predict,然后一个生产的 upstream (product)一个测试用的 upstream(test),ok,这时候一个 /predict/fake 请求过来了发生了什么呢

  1. 首先 /predict/fake 匹配到了 location,然后经过 lua 脚本分发请求,产生了两个子请求 /product/predict/fake 和 /test/predict/fake
  2. /product/predict/fake 匹配到新的 location,并且对这个 url 进行了 rewrite,变成了真实的后端请求地址 /predict,并且反向代理到 product 这个 upstream 下的 server,同理对 /test/predict/fake 也是一样的
  3. 两个子请求都有返回,但是在 lua 脚本中,只有 product 对应的请求被最终返回给了前端

需要注意的点:
反向代理时 rewrite 后的 url 会覆盖 proxy_pass 的 url

参考:

nginx 之 proxy_pass 接口转发的规则

相关文章

文章浏览阅读3.7k次,点赞2次,收藏5次。Nginx学习笔记一、N...
文章浏览阅读1.7w次,点赞14次,收藏61次。我们在使用容器的...
文章浏览阅读1.4k次。当用户在访问网站的过程中遇到404错误时...
文章浏览阅读2.7k次。docker 和 docker-compose 部署 nginx+...
文章浏览阅读1.3k次。5:再次启动nginx,可以正常启动,可以...
文章浏览阅读3.1w次,点赞105次,收藏182次。高性能:Nginx ...