复制线上真实流量,在不影响真实业务前提下,利用复制流量来作故障分析、性能定位、迁移评估等功能。具体功能包含:html
mirror: 中文为镜像的意思,这里指流量复制的目的地。java
正常安装nginx 1.13.4及后续版本。下面配置在1.14.1中验证经过。nginx
server { listen 80; server_name web1.www.com; # 源站配置 location / { access_log /data/nginx/1.14.1/logs/web1/access.log accesslog; mirror /mirror; mirror_request_body on;# Indicates whether the client request body is mirrored. default value is on. proxy_pass http://web1.upstream.name; } # 镜像站点配置 location /mirror { internal; # 内部配置 proxy_pass http://mirror.web1.upstream.name$request_uri; proxy_pass_request_body on; # Indicates whether the original request body is passed to the proxied server. default value is on proxy_set_header X-Original-URI $request_uri; # reset uri } }
默认是支持post复制的,须要经过配置来禁止web
server { listen 80; server_name web1.www.com; # 源站配置 location / { access_log /data/nginx/1.14.1/logs/web1/access.log accesslog; mirror /mirror; mirror_request_body off;# Indicates whether the client request body is mirrored. default value is on. proxy_pass http://web1.upstream.name; } # 镜像站点配置 location /mirror { # 判断请求方法,不是GET返回403 if ($request_method != GET) { return 403; } internal; # 内部配置 proxy_pass http://mirror.web1.upstream.name$request_uri; proxy_pass_request_body off; # Indicates whether the original request body is passed to the proxied server. default value is on proxy_set_header Content-Length ""; # mirror_request_body/proxy_pass_request_body都设置为off,则Conten-length须要设置为"",不然有坑 proxy_set_header X-Original-URI $request_uri; # 使用真实的url重置url } }
配置多分mirrorspring
server { listen 80; server_name web1.www.com; # 源站配置 location / { access_log /data/nginx/1.14.1/logs/web1/access.log accesslog; mirror /mirror; # 多加一份mirror,流量放大一倍 mirror /mirror; mirror_request_body on;# Indicates whether the client request body is mirrored. default value is on. proxy_pass http://web1.upstream.name; } # 镜像站点配置 location /mirror { internal; # 内部配置 proxy_pass http://mirror.web1.upstream.name$request_uri; proxy_pass_request_body on; # Indicates whether the original request body is passed to the proxied server. default value is on proxy_set_header X-Original-URI $request_uri; # reset uri } }
mirror中不支持配置access_log,解决方法:mirror-location跳转到server,在server中配置accesslog.json
server { listen 80; server_name web1.www.com; # 源站配置 location / { access_log /data/nginx/1.14.1/logs/web1/access.log accesslog; mirror /mirror; mirror_request_body off;# Indicates whether the client request body is mirrored. default value is on. proxy_pass http://web1.upstream.name; } # 镜像站点配置 location /mirror { internal; # 内部配置 # 跳转到下面的内部server proxy_pass http://127.0.0.1:10901$request_uri; proxy_pass_request_body off; # Indicates whether the original request body is passed to the proxied server. default value is on # Content-Length必须配置在mirror中不然无效 proxy_set_header Content-Length ""; # mirror_request_body/proxy_pass_request_body都设置为off,则Conten-length须要设置为"",不然有坑 proxy_set_header X-Original-URI $request_uri; # 使用真实的url重置url } } server { # server无法设置为内部 listen 127.0.0.1:10901; location / { # 判断放在server,使得post请求日志能够记录 if ($request_method != GET) { return 403; } access_log /data/nginx/1.14.1/logs/web1/access.log accesslog; proxy_pass http://mirror.web1.upstream.name; } }
利用jemeter,在相同环境,使用30个并发,各请求3000次get或post方法,参数同样,一组为有mirror配置,另外一组为没mirror配置,整体评估,mirror性能损失在5%之内,具体以下: tomcat
镜像配置不正确,致使复制操做没正常执行,此时nginx可能缺乏错误日志,严重影响调试,因此很是建议配置镜像日志,配置方法如4. mirror日志。部分错误配置下错误信息在error日志中。并发
若是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会报以下错误:mvc
"2018-11-08T17: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)