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动态转换图片大小生成缩略图
Mar 31 Servers
Nginx 过滤静态资源文件的访问日志的实现
Mar 31 Servers
Nginx配置Https安全认证的实现
May 26 Servers
Kubernetes部署实例并配置Deployment、网络映射、副本集
Apr 01 Servers
OpenStack虚拟机快照和增量备份实现方法
Apr 04 Servers
Linux下使用C语言代码搭建一个简单的HTTP服务器
Apr 13 Servers
Tomcat项目启动失败的原因和解决办法
Apr 20 Servers
Nginx 常用配置
May 15 Servers
windows10 家庭版下FTP服务器搭建教程
Aug 05 Servers
nginx配置指令之server_name的具体使用
Aug 14 Servers
ubuntu如何搭建vsftpd服务器
Dec 24 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
Symfony2学习笔记之插件格式分析
2016/03/17 PHP
php中的异常和错误浅析
2017/05/03 PHP
PHP获取HTTP body内容的方法
2018/12/31 PHP
javascript高级学习笔记整理
2011/08/14 Javascript
jquery淡化版banner异步图片文字效果切换图片特效
2014/04/08 Javascript
浅谈javascript的call()、apply()、bind()的用法
2016/02/21 Javascript
JQuery插件Marquee.js实现无缝滚动效果
2016/04/26 Javascript
前端设计师们最常用的JS代码汇总
2016/09/25 Javascript
js中class的点击事件没有效果的解决方法
2016/10/13 Javascript
JS对大量数据进行多重过滤的方法
2016/11/04 Javascript
关于axios返回空对象的问题解决
2017/04/04 Javascript
详解AngularJs路由之Ui-router-resolve(预加载)
2017/06/13 Javascript
jQuery完成表单验证的实例代码(纯代码)
2017/09/30 jQuery
JS实现的缓冲运动效果示例
2018/04/30 Javascript
微信小程序实现可长按移动控件
2020/11/01 Javascript
解决nuxt 自定义全局方法,全局属性,全局变量的问题
2020/11/05 Javascript
Python中的with...as用法介绍
2015/05/28 Python
Python中http请求方法库汇总
2016/01/06 Python
python快速建立超简单的web服务器的实现方法
2018/02/17 Python
python 巧用正则寻找字符串中的特定字符的位置方法
2018/05/02 Python
python 重命名轴索引的方法
2018/11/10 Python
如何使用python操作vmware
2019/07/27 Python
tensorflow实现tensor中满足某一条件的数值取出组成新的tensor
2020/01/04 Python
python无序链表删除重复项的方法
2020/01/17 Python
Django Xadmin多对多字段过滤实例
2020/04/07 Python
appium+python自动化配置(adk、jdk、node.js)
2020/11/17 Python
德国原装品牌香水、化妆品和手表网站:BRASTY.DE
2016/10/16 全球购物
Puccini乌克兰:购买行李箱、女士手袋网上商店
2020/08/06 全球购物
村委会贫困证明
2014/01/14 职场文书
护理毕业生自我鉴定
2014/02/11 职场文书
会务接待方案
2014/02/27 职场文书
护士长2014年终工作总结
2014/11/11 职场文书
自主招生自荐信格式范文
2015/03/25 职场文书
公司周年庆典致辞
2015/07/30 职场文书
用Python实现一个打字速度测试工具来测试你的手速
2021/05/28 Python
Java比较两个对象中全部属性值是否相等的方法
2021/08/07 Java/Android