Nginx流量拷贝ngx_http_mirror_module模块使用方法详解


Posted in Servers onApril 07, 2022

一、Nginx的ngx_http_mirror_module模块实现流量复制介绍

Nginx专门提供了ngx_http_mirror_module模块,用来实现流量拷贝。将生产环境的流量拷贝到预上线环境或测试环境,这样做有很多好处:

  • 可以验证功能是否正常,以及服务的性能;
  • 用真实有效的流量请求去验证,又不用造数据,不影响线上正常访问;
  • 相比于灰度发布,镜像流量不会影响真实流量;
  • 可以用来排查线上问题;
  • 重构,假如服务做了重构,这也是一种测试方式;

ngx_http_mirror_module模块就像是一个镜像站点一样,将所有的请求都收集起来,这个镜像站点就代表了所有真实有效的原始请求。有了这个镜像站点,后续就可以复现所有的请求,实现把线上的流程复制到别的地方。

ngx_http_mirror_module模块特性:

  • nginx 1.13.4及后续版本内置ngx_http_mirror_module模块,提供流量镜像(复制)的功能。
  • 支持流量放大,做法为:配置多份相同镜像。
  • 相比tcp-copy的优势:无需录制流量,实时可用;配置相当简单。
  • 源站请求,直接原路返回;正常配置下,mirror请求不影响源站请求及响应,源站nginx-server将流量复制到mirror站后,两者不再有任何交集。

二、Nginx编译安装,要加上ngx_http_mirror_module模块

下面是Nginx解压后,编译安装的示例

# ./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

三、Nginx流量拷贝的配置示例

upstream kevin-order {
  server 127.0.0.1:8088;
}

upstream kevin-customer {
  server 127.0.0.1:8089;
}

upstream kevin-mirror1 {
    server 172.16.60.230:8088;
}

upstream kevin-mirror2 {
    server 172.16.60.230:8089;
}

server {
    listen 80;
    server_name  kevin.com;
    access_log  /usr/local/nginx/logs/kevin.com-access.log main;
    error_log   /usr/local/nginx/logs/kevin.com-error.log;

  # 源站点1
    location /order {
        proxy_pass http://kevin-order;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 复制请求体
        mirror_request_body on;
        # 流量复制
        mirror /mirror1;
    }

    # 源站点2
    location /customer {
        proxy_pass http://kevin-customer;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        mirror_request_body on;
        mirror /mirror2;
    }

    # 镜像站点1
    location /mirror1 {
        proxy_pass http://kevin-mirror1$request_uri;
        proxy_pass_request_body on;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # 镜像站点2
    location /mirror2 {
        proxy_pass http://kevin-mirror2$request_uri;
        proxy_pass_request_body on;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

配置说明:上面配置中,将访问http://kevin.com/order、http://kevin.com/customer的流量分别复制到172.16.60.230服务器的8088和8089端口。

四、Nginx使用ngx_http_mirror_module模块进行流量拷贝的配置技巧

1)Nginx复制GET及POST请求流量

server {
        listen       80;
        server_name  kevin.com;
        # 源站配置
        location / {
                access_log  /usr/local/nginx/logs/access.log  accesslog;
                mirror /mirror;
                mirror_request_body on;
                proxy_pass http://kevin.upstream.name;
        }
        # 镜像站点配置
        location /mirror {
                internal; # 内部配置
                proxy_pass http://mirror.kevin.upstream.name$request_uri;
                proxy_pass_request_body on;
                proxy_set_header X-Original-URI $request_uri; #使用真实的url重置url
        }
}

2)Nginx不允许复制POST请求流量

默认是支持POST流量复制的,需要通过下面配置来禁止。

server {
        listen       80;
        server_name  kevin.com;

        # 源站配置
        location / {
                access_log  /usr/local/nginx/logs/access.log  accesslog;
                mirror /mirror;
                mirror_request_body off;
                proxy_pass http://kevin.upstream.name;
        }

        # 镜像站点配置
        location /mirror {
                # 判断请求方法,不是GET返回403
                if ($request_method != GET) {
                    return 403;
                }
                internal;  #内部配置
                proxy_pass http://mirror.kevin.upstream.name$request_uri;
                proxy_pass_request_body off;
                # mirror_request_body和proxy_pass_request_body都设置为off,则Conten-length需要设置为"",否则有坑!
                proxy_set_header Content-Length "";
                proxy_set_header X-Original-URI $request_uri; # 使用真实的url重置url
        }
}

3)拷贝流量放大

配置多分mirror镜像点

server {
        listen       80;
        server_name  kevin.com;
        # 源站配置
        location / {
                access_log  /usr/local/nginx/logs/access.log  accesslog;
                mirror /mirror;
                # 多加一份mirror,流量放大一倍
                mirror /mirror;
                mirror_request_body on;
                proxy_pass http://kevin.upstream.name;
        }
        # 镜像站点配置
        location /mirror {
                internal; # 内部配置
                proxy_pass http://mirror.kevin.upstream.name$request_uri;
                proxy_pass_request_body on;
                proxy_set_header X-Original-URI $request_uri;  #使用真实的url重置url
        }
}

4)配置mirror镜像日志

mirror中不支持配置access_log,解决方法:mirror-location跳转到server,在server中配置accesslog。

server {
        listen       80;
        server_name  kevin.com;
        # 源站配置
        location / {
                access_log  /usr/local/nginx/logs/access.log  accesslog;
                mirror /mirror;
                mirror_request_body on;
                proxy_pass http://kevin.upstream.name;
        }
        # 镜像站点配置
        location /mirror {
                internal; # 内部配置
                # 跳转到下面的内部server
                proxy_pass http://127.0.0.1:10992$request_uri;
                proxy_pass_request_body off;
                proxy_set_header Content-Length "";
                proxy_set_header X-Original-URI $request_uri; #使用真实的url重置url
        }

server {
    # server没法设置为内部
    listen 127.0.0.1:10992;
    location / {
        # 判断放在server,使得post请求日志可以记录
        if ($request_method != GET) {
            return 403;
        }
        access_log /usr/local/nginx/logs/access.log accesslog;
        proxy_pass http://mirror.kevin.upstream.name;
    }

}

五、Nginx流量拷贝的注意事项

1)mirror镜像配置日志

镜像配置不正确,导致流量复制操作没正常执行。如果mirror镜像配置缺少日志,会严重影响调试。所以强烈建议配置镜像日志,配置方法如如上"配置mirror镜像日志"。部分错误配置的错误信息在在error日志中。

2)mirror_request_body/proxy_pass_request_body与Content-Length需配置一致

如果mirror_request_body或者proxy_pass_request_body设置为off,则Content-Length必须设置为"",因为nginx(mirror_request_body)tomcat(mirror_request_body)处理post请求时,会根据Content-Length获取请求体,如果Content-Length不为空,而由于mirror_request_body或者proxy_pass_request_body设置为off,处理方以为post有内容,当request_body中没有,处理方会一直等待至超时,则前者为off,nginx会报upstream请求超时;后者为off,tomcat会报如下错误:

"2020-11-18T17:26:36.803+08:00" "331632b86ec64b829672066a96fc6324"      "department"        "group"   "project_name"        "hostname"    "127.0.0.1"     ""      "/post" "p=11"  "-"     "PostmanRuntime/7.1.1"  "ERROR" "xxx.GlobalControllerAdvice"       "operateExp"    "-"     "26"    "xxxx.GlobalControllerAdvice"       "unknown"       "org.springframework.http.converter.HttpMessageNotReadableException"    "I/O error while reading input message; nested exception is java.net.SocketTimeoutException"    "GlobalControllerAdvice中捕获全局异常"  "org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.net.SocketTimeoutException
        at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:229)
        at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:150)
        at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:128)
        at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
        at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
Servers 相关文章推荐
详解Nginx 工作原理
Mar 31 Servers
Apache Calcite 实现方言转换的代码
Apr 24 Servers
nginx部署多前端项目的几种方法
May 25 Servers
Nginx实现高可用集群构建(Keepalived+Haproxy+Nginx)
May 27 Servers
解决xampp安装后Apache无法启动
Mar 21 Servers
Kubernetes控制节点的部署
Apr 01 Servers
nginx location 带斜杠【 / 】与不带的区别
Apr 13 Servers
Windows server 2012 R2 安装IIS服务器
Apr 29 Servers
Nginx本地配置SSL访问的实例教程
May 30 Servers
解决Vmware虚拟机安装centos8报错“Section %Packages Does Not End With %End. Pane Is Dead”
Jun 01 Servers
vscode内网访问服务器的方法
Jun 28 Servers
使用 DataAnt 监控 Apache APISIX的原理解析
Jul 07 Servers
忘记Grafana不要紧2种Grafana重置admin密码方法详细步骤
Apr 07 #Servers
Linux、ubuntu系统下查看显卡型号、显卡信息详解
Nginx速查手册及常见问题
从零开始在Centos7上部署SpringBoot项目
CentOS7和8下安装Maven3.8.4
CentOS下安装Jenkins的完整步骤
基于Apache Hudi在Google云构建数据湖平台的思路详解
You might like
3种平台下安装php4经验点滴
2006/10/09 PHP
php5中date()得出的时间为什么不是当前时间的解决方法
2008/06/30 PHP
在Windows XP下安装Apache+MySQL+PHP环境
2015/02/22 PHP
Nginx下配置codeigniter框架方法
2015/04/07 PHP
php实现比较两个文件夹异同的方法
2015/06/18 PHP
PHP的静态方法与普通方法用法实例分析
2019/09/26 PHP
圣诞节Merry Christmas给博客添加浪漫的下雪效果基于jquery实现
2012/12/27 Javascript
jquery如何获取复选框的值
2013/12/12 Javascript
JavaScript设计模式之装饰者模式介绍
2014/12/28 Javascript
jQuery的选择器中的通配符[id^='code']或[name^='code']及jquery选择器总结
2015/12/24 Javascript
购物车前端开发(jQuery和bootstrap3)
2016/08/27 Javascript
Bootstrap实现导航栏的2种方式
2016/11/28 Javascript
解决canvas画布使用fillRect()时高度出现双倍效果的问题
2017/08/03 Javascript
让网站自动生成章节目录索引的多个js代码
2018/01/07 Javascript
vue动态删除从数据库倒入列表的某一条方法
2018/09/29 Javascript
小程序测试后台服务的方法(ngrok)
2019/03/08 Javascript
[04:03]DOTA2英雄梦之声_第02期_风暴之灵
2014/06/30 DOTA
[02:41]2015国际邀请赛中国区预选赛观战指南
2015/05/20 DOTA
python获取Linux下文件版本信息、公司名和产品名的方法
2014/10/05 Python
Python2实现的LED大数字显示效果示例
2017/09/04 Python
Python编程pygal绘图实例之XY线
2017/12/09 Python
python 中字典嵌套列表的方法
2018/07/03 Python
python根据url地址下载小文件的实例
2018/12/18 Python
Python使用while循环花式打印乘法表
2019/01/28 Python
Python配置虚拟环境图文步骤
2019/05/20 Python
python中使用ctypes调用so传参设置遇到的问题及解决方法
2019/06/19 Python
python之信息加密题目详解
2019/06/26 Python
Python Django简单实现session登录注销过程详解
2019/08/06 Python
Anaconda之conda常用命令介绍(安装、更新、删除)
2019/10/06 Python
PyQT5 实现快捷键复制表格数据的方法示例
2020/06/19 Python
PyCharm2019 安装和配置教程详解附激活码
2020/07/31 Python
使用HTML5进行SVG矢量图形绘制的入门教程
2016/02/19 HTML / CSS
爱心捐款倡议书
2014/04/14 职场文书
工资收入证明样本(5篇)
2014/09/16 职场文书
人身损害赔偿协议书
2016/03/22 职场文书
基于Python编写简易版的天天跑酷游戏的示例代码
2022/03/23 Python