`

Nginx流量复制/AB测试/协程

阅读更多

在实际开发中经常涉及到项目的升级,而该升级不能简单的上线就完事了,需要验证该升级是否兼容老的上线,因此可能需要并行运行两个项目一段时间进行数据比对和校验,待没问题后再进行上线。这其实就需要进行流量复制,把流量复制到其他服务器上,一种方式是使用如tcpcopy引流;另外我们还可以使用nginx的HttpLuaModule模块中的ngx.location.capture_multi进行并发执行来模拟复制。

 

构造两个服务

Java代码  收藏代码
  1. location /test1 {  
  2.     keepalive_timeout 60s;   
  3.     keepalive_requests 1000;  
  4.     content_by_lua '  
  5.         ngx.print("test1 : ", ngx.req.get_uri_args()["a"])  
  6.         ngx.log(ngx.ERR, "request test1")  
  7.     ';  
  8. }  
  9. location /test2 {  
  10.     keepalive_timeout 60s;   
  11.     keepalive_requests 1000;  
  12.     content_by_lua '  
  13.         ngx.print("test2 : ", ngx.req.get_uri_args()["a"])  
  14.         ngx.log(ngx.ERR, "request test2")  
  15.     ';  
  16. }  

  

通过ngx.location.capture_multi调用

Java代码  收藏代码
  1. location /test {  
  2.      lua_socket_connect_timeout 3s;  
  3.      lua_socket_send_timeout 3s;  
  4.      lua_socket_read_timeout 3s;  
  5.      lua_socket_pool_size 100;  
  6.      lua_socket_keepalive_timeout 60s;  
  7.      lua_socket_buffer_size 8k;  
  8.   
  9.      content_by_lua '  
  10.          local res1, res2 = ngx.location.capture_multi{  
  11.                { "/test1", { args = ngx.req.get_uri_args() } },  
  12.                { "/test2", { args = ngx.req.get_uri_args()} },  
  13.          }  
  14.          if res1.status == ngx.HTTP_OK then  
  15.              ngx.print(res1.body)  
  16.          end  
  17.          if res2.status ~= ngx.HTTP_OK then  
  18.             --记录错误  
  19.          end  
  20.      ';  
  21. }  

此处可以根据需求设置相应的超时时间和长连接连接池等;ngx.location.capture底层通过cosocket实现,而其支持Lua中的协程,通过它可以以同步的方式写非阻塞的代码实现。

 

此处要考虑记录失败的情况,对失败的数据进行重放还是放弃根据自己业务做处理。

 

AB测试

AB测试即多版本测试,有时候我们开发了新版本需要灰度测试,即让一部分人看到新版,一部分人看到老版,然后通过访问数据决定是否切换到新版。比如可以通过根据区域、用户等信息进行切版本。

 

比如京东商城有一个cookie叫做__jda,该cookie是在用户访问网站时种下的,因此我们可以拿到这个cookie,根据这个cookie进行版本选择。

 

比如两次清空cookie访问发现第二个数字串是变化的,即我们可以根据第二个数字串进行判断。

__jda=122270672.1059377902.1425691107.1425691107.1425699059.1

__jda=122270672.556927616.1425699216.1425699216.1425699216.1。

 

判断规则可以比较多的选择,比如通过尾号;要切30%的流量到新版,可以通过选择尾号为1,3,5的切到新版,其余的还停留在老版。

 

1、使用map选择版本 

Java代码  收藏代码
  1. map $cookie___jda $ab_key {  
  2.     default                                       "0";  
  3.     ~^\d+\.\d+(?P<k>(1|3|5))\.                    "1";  
  4. }  

使用map映射规则,即如果是到新版则等于"1",到老版等于“0”; 然后我们就可以通过ngx.var.ab_key获取到该数据。

Java代码  收藏代码
  1. location /abtest1 {  
  2.     if ($ab_key = "1") {  
  3.         echo_location /test1 ngx.var.args;  
  4.     }  
  5.     if ($ab_key = "0") {  
  6.         echo_location /test2 ngx.var.args;  
  7.     }  
  8. }  

此处也可以使用proxy_pass到不同版本的服务器上 

Java代码  收藏代码
  1. location /abtest2 {  
  2.     if ($ab_key = "1") {  
  3.         rewrite ^ /test1 break;  
  4.         proxy_pass http://backend1;  
  5.     }  
  6.     rewrite ^ /test2 break;  
  7.     proxy_pass http://backend2;  
  8. }  

 

2、直接在Lua中使用lua-resty-cookie获取该Cookie进行解析

首先下载lua-resty-cookie

Java代码  收藏代码
  1. cd /usr/example/lualib/resty/  
  2. wget https://raw.githubusercontent.com/cloudflare/lua-resty-cookie/master/lib/resty/cookie.lua  

 

Java代码  收藏代码
  1. location /abtest3 {  
  2.     content_by_lua '  
  3.   
  4.          local ck = require("resty.cookie")  
  5.          local cookie = ck:new()  
  6.          local ab_key = "0"  
  7.          local jda = cookie:get("__jda")  
  8.          if jda then  
  9.              local v = ngx.re.match(jda, [[^\d+\.\d+(1|3|5)\.]])  
  10.              if v then  
  11.                 ab_key = "1"  
  12.              end  
  13.          end  
  14.   
  15.          if ab_key == "1" then  
  16.              ngx.exec("/test1", ngx.var.args)  
  17.          else  
  18.              ngx.print(ngx.location.capture("/test2", {args = ngx.req.get_uri_args()}).body)  
  19.          end  
  20.     ';  
  21.   
  22. }  

 首先使用lua-resty-cookie获 取cookie,然后使用ngx.re.match进行规则的匹配,最后使用ngx.exec或者ngx.location.capture进行处理。此 处同时使用ngx.exec和ngx.location.capture目的是为了演示,此外没有对ngx.location.capture进行异常处 理。

 

协程

Lua中没有线程和异步编程编程的概念,对于并发执行提供了协程的概念,个人认为协程是在A运行中发现自己忙则把CPU使用权让出来给B使用,最后A能从中断位置继续执行,本地还是单线程,CPU独占的;因此如果写网络程序需要配合非阻塞I/O来实现。

 

ngx_lua 模块对协程做了封装,我们可以直接调用ngx.thread API使用,虽然称其为“轻量级线程”,但其本质还是Lua协程。该API必须配合该ngx_lua模块提供的非阻塞I/O API一起使用,比如我们之前使用的ngx.location.capture_multi和lua-resty-redis、lua-resty- mysql等基于cosocket实现的都是支持的。

 

通过Lua协程我们可以并发的调用多个接口,然后谁先执行成功谁先返回,类似于BigPipe模型。

 

1、依赖的API 

Java代码  收藏代码
  1. location /api1 {  
  2.     echo_sleep 3;  
  3.     echo api1 : $arg_a;  
  4. }  
  5. location /api2 {  
  6.     echo_sleep 3;  
  7.     echo api2 : $arg_a;  
  8. }  

 我们使用echo_sleep等待3秒。

 

2、串行实现

Java代码  收藏代码
  1. location /serial {  
  2.     content_by_lua '  
  3.         local t1 = ngx.now()  
  4.         local res1 = ngx.location.capture("/api1", {args = ngx.req.get_uri_args()})  
  5.         local res2 = ngx.location.capture("/api2", {args = ngx.req.get_uri_args()})  
  6.         local t2 = ngx.now()  
  7.         ngx.print(res1.body, "<br/>", res2.body, "<br/>", tostring(t2-t1))  
  8.     ';  
  9. }  

即一个个的调用,总的执行时间在6秒以上,比如访问http://192.168.1.2/serial?a=22

Java代码  收藏代码
  1. api1 : 22   
  2. api2 : 22   
  3. 6.0040001869202  

 

3、ngx.location.capture_multi实现

Java代码  收藏代码
  1. location /concurrency1 {  
  2.     content_by_lua '  
  3.         local t1 = ngx.now()  
  4.         local res1,res2 = ngx.location.capture_multi({  
  5.               {"/api1", {args = ngx.req.get_uri_args()}},  
  6.               {"/api2", {args = ngx.req.get_uri_args()}}  
  7.   
  8.         })  
  9.         local t2 = ngx.now()  
  10.         ngx.print(res1.body, "<br/>", res2.body, "<br/>", tostring(t2-t1))  
  11.     ';  
  12. }  

直接使用ngx.location.capture_multi来实现,比如访问http://192.168.1.2/concurrency1?a=22

Java代码  收藏代码
  1. api1 : 22   
  2. api2 : 22   
  3. 3.0020000934601  

    

4、协程API实现 

Java代码  收藏代码
  1. location /concurrency2 {  
  2.     content_by_lua '  
  3.         local t1 = ngx.now()  
  4.         local function capture(uri, args)  
  5.            return ngx.location.capture(uri, args)  
  6.         end  
  7.         local thread1 = ngx.thread.spawn(capture, "/api1", {args = ngx.req.get_uri_args()})  
  8.         local thread2 = ngx.thread.spawn(capture, "/api2", {args = ngx.req.get_uri_args()})  
  9.         local ok1, res1 = ngx.thread.wait(thread1)  
  10.         local ok2, res2 = ngx.thread.wait(thread2)  
  11.         local t2 = ngx.now()  
  12.         ngx.print(res1.body, "<br/>", res2.body, "<br/>", tostring(t2-t1))  
  13.     ';  
  14. }  

使用ngx.thread.spawn创建一个轻量级线程,然后使用ngx.thread.wait等待该线程的执行成功。比如访问http://192.168.1.2/concurrency2?a=22

Java代码  收藏代码
  1. api1 : 22   
  2. api2 : 22   
  3. 3.0030000209808  

   

其有点类似于Java中的线程池执行模型,但不同于线程池,其每次只执行一个函数,遇到IO等待则让出CPU让下一个执行。我们可以通过下面的方式实现任意一个成功即返回,之前的是等待所有执行成功才返回。

Java代码  收藏代码
  1. local  ok, res = ngx.thread.wait(thread1, thread2) 
分享到:
评论

相关推荐

    Nginx通过/etc/init.d/nginx方式启停【nginx配置文件】

    vi /etc/init.d/nginx 修改nginx后 chmod +x /etc/init.d/nginx /sbin/chkconfig nginx on sudo /sbin/chkconfig --list nginx /etc/init.d/nginx start

    Linux安装nginx/mysql/php/zabbix

    基于CentOS Linux release 7.4.1708手把手搭建环境nginx/mysql/php/zabbix

    Nginx RPM 包定制制作

    /application/nginx/sbin/nginx ss -lntup|grep nginx ps -ef|grep nginx|grep -v grep netstat -lntup|grep nginx|grep -v grep curl 127.0.0.1 mkdir -p /server/scripts cd /server/scripts/ fpm -s ...

    重启或杀掉Nginx进程后丢失nginx.pid的解决办法

    Restarting nginx daemon: nginxcat: /usr/local/nginx/logs/nginx.pid: No such file or directorykill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec … or kill -l [sigspec]nginx not ...

    LNMP(Nginx/MySQL/PHP)

    LNMP(Nginx/MySQL/PHP) LNMP代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构。 无需一个一个的输入命令,无需值守,编译安装优化编译参数,提高性能,解决不必要的软件间依赖,特别针对配置自动优化 作为...

    开机自起nginx

    ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf #ExecReload=/usr/local/nginx/sbin/nginx -s ...

    NGINX流量拷贝方式进行并发测试.conf

    有时需要进行并发测试,需要排除本地机器等环境问题,可以使用web服务器NGINX的流量拷贝方式把请求进行翻倍,从而达到并发目的

    关于修改nginx-1.12.2/src/os/unix下的代码.png

    关于修改nginx-1.12.2/src/os/unix下的代码

    Nginx配置统计流量带宽请求及记录实时请求状态的方法

    流量带宽请求状态统计 ngx_req_status用来展示nginx请求状态信息,类似于apache的status,nginx自带的模块只能显示连接数等等信息,我们并不能知道到底有哪些请求、以及各url域名所消耗的带宽是多少。ngx_req_status...

    Nginx配置TCP/UDP调度器.doc

    使用3台centos7虚拟机,其中一台作为Nginx代理服务器,IP地址为192.168.0.11,一台SSH服务器IP地址为192.168.0.120,客户端测试主机IP地址为192.168.0.63

    Ubuntu Debian(NGINX/PHP/MYSQL)快速配置工具LNMP云安装

    LNMP云安装Ubuntu Debian(NGINX/PHP/MYSQL)快速配置工具经测试,整个过程约2-5分钟完成。各种组件均使用最新稳定版。加入php host功能限制每个网站的访问目录,防止跨目录,更安全!解决nginx 0day漏洞! ...

    linux快速nginx配置

    nginx=”/usr/local/nginx/sbin/nginx” //修改成nginx执行程序的路径。 NGINX_CONF_FILE=”/usr/local/nginx/conf/nginx.conf” //修改成nginx.conf文件的路径。 保存后设置文件的执行权限 [root@localhost ~]# ...

    nginx安装教程

    sudo ./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 \ --with-pcre=/usr/local/src/pcre-8.41 \ ...

    Openwrt Nginx交叉编译Makefile/nginx.init/patch

    这个压缩包里面包含了在openwrt交叉编译nginx-1.8.0所需要的Makefile和patch。 作为文章http://blog.csdn.net/ping1214/article/details/45397729的附件。

    nginx_1.16.1-1_xenial_amd64.deb (ubuntu16.0.4)

    B、修改配置文件/etc/nginx/conf.d/default.conf 2.启动Nginx服务 sudo /etc/init.d/nginx start 3.优雅停止Nginx服务 sudo /etc/init.d/nginx quit 4.加载最新配置 sudo /etc/init.d/nginx reload 5.立即停止Nginx...

    nginx安装文档.pdf

    https://github.com/nginx/nginx/releases 下载各个版本的nginx的地址: http://nginx.org/download/ 下载Nginx 上传nginx压缩包 通过rz命令上传⽂件:nginx-1.15.6.tar.gz 2、安装第三⽅软件 2.1、安装PCRE PCRE...

    Nginx Docker安装配置

    $ docker cp lw-nginx:/etc/nginx/nginx.conf /colorfulfrog/nginx/config –-将容器中的nginx.conf拷贝到宿主目录下 $ docker cp lw-nginx:/usr/share/nginx/html /colorfulfrog/nginx –-将容器中的html目录内容...

    nginx/ftp/tomcat

    详细教程:http://blog.csdn.net/ifwinds/article/details/52164530 包含以下内容: Tomcat:apache-tomcat-7.0.57.tar Nginx:pcre-8.34.tar ,nginx-1.6.2.tar Ftp:vsftpd-2.2.2-6.el6_0.1.x86_64.rpm

    Nginx启动常见错误及解决方法

    登陆服务器之后进到nginx使用./nginx -s reload重新读取配置文件,发现报nginx: [error] open() “/usr/local/nginx/logs/nginx.pid” failed (2: No such file or directory)错误,进到logs文件发现的确没有nginx....

    stream-echo-nginx-module, 用于 Nginx的tcp/流回显模块( ngx_http_echo_module的端口).zip

    stream-echo-nginx-module, 用于 Nginx的tcp/流回显模块( ngx_http_echo_module的端口) 电子邮件名称ngx_stream_echo - Nginx/流回波模块( ngx_http_echo模块的端口)目录名称版本概要说明示例 1示例 2示例 3示例 4...

Global site tag (gtag.js) - Google Analytics