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
Nginx域名转发https访问的实现
Mar 31 Servers
Nginx已编译的nginx-添加新模块
Apr 01 Servers
使用nginx配置访问wgcloud的方法
Jun 26 Servers
Nginx stream 配置代理(Nginx TCP/UDP 负载均衡)
Nov 17 Servers
配置Kubernetes外网访问集群
Mar 31 Servers
nginx 配置指令之location使用详解
May 25 Servers
永中文档在线转换预览基于nginx配置部署方案
Jun 10 Servers
Windows server 2012 NTP时间同步的实现
Jun 25 Servers
centos环境下nginx高可用集群的搭建指南
Jul 23 Servers
SpringBoot前端后端分离之Nginx服务器下载安装过程
Aug 14 Servers
Tomcat安装使用及部署Web项目的3种方法汇总
Aug 14 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
ajax取消挂起请求的处理方法
2013/03/18 PHP
怎样给PHP源代码加密?PHP二进制加密与解密的解决办法
2013/04/22 PHP
MySql数据库查询结果用表格输出PHP代码示例
2015/03/20 PHP
详细解读php的命名空间(一)
2018/02/21 PHP
Mootools 1.2教程 定时器和哈希简介
2009/09/15 Javascript
纯js实现背景图片切换效果代码
2010/11/14 Javascript
Js从头学起(基本数据类型和引用类型的参数传递详细分析)
2012/02/16 Javascript
js 回车提交表单两种实现方法
2012/12/31 Javascript
JS实现跟随鼠标闪烁转动色块的方法
2015/02/26 Javascript
JavaScript基础重点(必看)
2016/07/09 Javascript
微信小程序开发之入门实例教程篇
2017/03/07 Javascript
Vue项目全局配置页面缓存之按需读取缓存的实现详解
2018/08/01 Javascript
vue-cli 使用axios的操作方法及整合axios的多种方法
2018/09/12 Javascript
Vue.js 图标选择组件实践详解
2018/12/03 Javascript
微信小程序中转义字符的处理方法
2019/03/28 Javascript
vue前后分离调起微信支付
2019/07/29 Javascript
layui对工具条进行选择性的显示方法
2019/09/19 Javascript
Vue 中 filter 与 computed 的区别与用法解析
2019/11/21 Javascript
使用typescript改造koa开发框架的实现
2020/02/04 Javascript
js删除对象中的某一个字段的方法实现
2021/01/11 Javascript
python采集微信公众号文章
2018/12/20 Python
python并发编程多进程 互斥锁原理解析
2019/08/20 Python
Python 、Pycharm、Anaconda三者的区别与联系、安装过程及注意事项
2019/10/11 Python
canvas 下载二维码和图片加水印的方法
2018/03/21 HTML / CSS
白俄罗斯大卖场:21vek.by
2019/07/25 全球购物
易程科技软件测试笔试
2013/03/24 面试题
平面设计求职信
2014/03/10 职场文书
手机银行营销方案
2014/03/14 职场文书
《泉水》教学反思
2014/04/11 职场文书
白酒营销策划方案
2014/08/17 职场文书
2014年员工工作总结范文
2014/11/18 职场文书
怎样写观后感
2015/06/19 职场文书
python OpenCV学习笔记
2021/03/31 Python
css3实现的加载动画效果
2021/04/07 HTML / CSS
Python绘制地图神器folium的新人入门指南
2021/05/23 Python
恶魔之树最顶端的三颗果实 震震果实上榜,第一可以制造岩浆
2022/03/18 日漫