第八章 (Nginx+Lua)流量复制/AB测试/协程
流量复制
在实际开发中经常涉及到项目的升级,而该升级不能简单的上线就完事了,需要验证该升级是否兼容老的上线,因此可能需要并行运行两个项目一段时间进行数据比对和校验,待没问题后再进行上线。这其实就需要进行流量复制,把流量复制到其他服务器上,一种方式是使用如tcpcopy引流;另外我们还可以使用nginx的HttpLuaModule模块中的ngx.location.capture_multi进行并发执行来模拟复制。
构造两个服务
- location /test1 {
- keepalive_timeout 60s;
- keepalive_requests 1000;
- content_by_lua '
- ngx.print("test1 : ", ngx.req.get_uri_args()["a"])
- ngx.log(ngx.ERR, "request test1")
- ';
- }
- location /test2 {
- keepalive_timeout 60s;
- keepalive_requests 1000;
- content_by_lua '
- ngx.print("test2 : ", ngx.req.get_uri_args()["a"])
- ngx.log(ngx.ERR, "request test2")
- ';
- }
通过ngx.location.capture_multi调用
- location /test {
- lua_socket_connect_timeout 3s;
- lua_socket_send_timeout 3s;
- lua_socket_read_timeout 3s;
- lua_socket_pool_size 100;
- lua_socket_keepalive_timeout 60s;
- lua_socket_buffer_size 8k;
- content_by_lua '
- local res1, res2 = ngx.location.capture_multi{
- { "/test1", { args = ngx.req.get_uri_args() } },
- { "/test2", { args = ngx.req.get_uri_args()} },
- }
- if res1.status == ngx.HTTP_OK then
- ngx.print(res1.body)
- end
- if res2.status ~= ngx.HTTP_OK then
- --记录错误
- end
- ';
- }
此处可以根据需求设置相应的超时时间和长连接连接池等;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选择版本
- map $cookie___jda $ab_key {
- default "0";
- ~^\d+\.\d+(?P<k>(1|3|5))\. "1";
- }
使用map映射规则,即如果是到新版则等于"1",到老版等于“0”; 然后我们就可以通过ngx.var.ab_key获取到该数据。
- location /abtest1 {
- if ($ab_key = "1") {
- echo_location /test1 ngx.var.args;
- }
- if ($ab_key = "0") {
- echo_location /test2 ngx.var.args;
- }
- }
此处也可以使用proxy_pass到不同版本的服务器上
- location /abtest2 {
- if ($ab_key = "1") {
- rewrite ^ /test1 break;
- proxy_pass http://backend1;
- }
- rewrite ^ /test2 break;
- proxy_pass http://backend2;
- }
2、直接在Lua中使用lua-resty-cookie获取该Cookie进行解析
首先下载lua-resty-cookie
- cd /usr/example/lualib/resty/
- wget https://raw.githubusercontent.com/cloudflare/lua-resty-cookie/master/lib/resty/cookie.lua
- location /abtest3 {
- content_by_lua '
- local ck = require("resty.cookie")
- local cookie = ck:new()
- local ab_key = "0"
- local jda = cookie:get("__jda")
- if jda then
- local v = ngx.re.match(jda, [[^\d+\.\d+(1|3|5)\.]])
- if v then
- ab_key = "1"
- end
- end
- if ab_key == "1" then
- ngx.exec("/test1", ngx.var.args)
- else
- ngx.print(ngx.location.capture("/test2", {args = ngx.req.get_uri_args()}).body)
- end
- ';
- }
首先使用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
- location /api1 {
- echo_sleep 3;
- echo api1 : $arg_a;
- }
- location /api2 {
- echo_sleep 3;
- echo api2 : $arg_a;
- }
我们使用echo_sleep等待3秒。
2、串行实现
- location /serial {
- content_by_lua '
- local t1 = ngx.now()
- local res1 = ngx.location.capture("/api1", {args = ngx.req.get_uri_args()})
- local res2 = ngx.location.capture("/api2", {args = ngx.req.get_uri_args()})
- local t2 = ngx.now()
- ngx.print(res1.body, "<br/>", res2.body, "<br/>", tostring(t2-t1))
- ';
- }
即一个个的调用,总的执行时间在6秒以上,比如访问http://192.168.1.2/serial?a=22
- api1 : 22
- api2 : 22
- 6.0040001869202
3、ngx.location.capture_multi实现
- location /concurrency1 {
- content_by_lua '
- local t1 = ngx.now()
- local res1,res2 = ngx.location.capture_multi({
- {"/api1", {args = ngx.req.get_uri_args()}},
- {"/api2", {args = ngx.req.get_uri_args()}}
- })
- local t2 = ngx.now()
- ngx.print(res1.body, "<br/>", res2.body, "<br/>", tostring(t2-t1))
- ';
- }
直接使用ngx.location.capture_multi来实现,比如访问http://192.168.1.2/concurrency1?a=22
- api1 : 22
- api2 : 22
- 3.0020000934601
4、协程API实现
- location /concurrency2 {
- content_by_lua '
- local t1 = ngx.now()
- local function capture(uri, args)
- return ngx.location.capture(uri, args)
- end
- local thread1 = ngx.thread.spawn(capture, "/api1", {args = ngx.req.get_uri_args()})
- local thread2 = ngx.thread.spawn(capture, "/api2", {args = ngx.req.get_uri_args()})
- local ok1, res1 = ngx.thread.wait(thread1)
- local ok2, res2 = ngx.thread.wait(thread2)
- local t2 = ngx.now()
- ngx.print(res1.body, "<br/>", res2.body, "<br/>", tostring(t2-t1))
- ';
- }
使用ngx.thread.spawn创建一个轻量级线程,然后使用ngx.thread.wait等待该线程的执行成功。比如访问http://192.168.1.2/concurrency2?a=22
- api1 : 22
- api2 : 22
- 3.0030000209808
其有点类似于Java中的线程池执行模型,但不同于线程池,其每次只执行一个函数,遇到IO等待则让出CPU让下一个执行。我们可以通过下面的方式实现任意一个成功即返回,之前的是等待所有执行成功才返回。
- local ok, res = ngx.thread.wait(thread1, thread2)
第八章 (Nginx+Lua)流量复制/AB测试/协程的更多相关文章
- 高并发 Nginx+Lua OpenResty系列(11)——流量复制/AB测试/协程
流量复制 在实际开发中经常涉及到项目的升级,而该升级不能简单的上线就完事了,需要验证该升级是否兼容老的上线,因此可能需要并行运行两个项目一段时间进行数据比对和校验,待没问题后再进行上线.这其实就需要进 ...
- Lua基础之coroutine(协程)
概括:1.创建协程2.coroutine的函数3.coroutine的基本流程4.yield对coroutine流程的干预5.resume, function()以及yield之间的参数传递和返回值传 ...
- lua学习笔记13:协程具体解释和举例
一.coroutine.create创建协程 參数是协程的主函数,返回一个thread对象 co = coroutine.create(function() print("coroutine ...
- [lua]异步串行流程*协程
local function param_pack( params, callback ) table.insert(params, callback) return params end local ...
- 跟我学OpenResty(Nginx+Lua)开发目录贴 (转)
使用Nginx+Lua开发近一年的时间,学习和实践了一些Nginx+Lua开发的架构,为了让更多人使用Nginx+Lua架构开发,利用春节期间总结了一份基本的学习教程,希望对大家有用.也欢迎谈探讨学习 ...
- (zt)Lua的多任务机制——协程(coroutine)
原帖:http://blog.csdn.net/soloist/article/details/329381 并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上 ...
- Lua的多任务机制——协程(coroutine)
并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上有这么两种多任务技术,一种是抢占式多任务(preemptive multitasking),它让操作系统来决定 ...
- Python线程和协程CPU资源利用率测试
前言介绍 协程 ,又称为微线程,它是实现多任务的另一种方式,只不过是比线程更小的执行单元.因为它自带CPU的上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程.通俗的理解: 在一个线程中 ...
- Nginx流量复制
1. 需求 将生产环境的流量拷贝到预上线环境或测试环境,这样做有很多好处,比如: 可以验证功能是否正常,以及服务的性能: 用真实有效的流量请求去验证,又不用造数据,不影响线上正常访问: 这跟灰度发布还 ...
- Nginx + LUA下流量拦截算法
前言 每逢大促必压测,每逢大促必限流,这估计是电商人的常态.每次大促期间,业务流量是平时的几倍十几倍,大促期间大部分业务都会集中在购物车结算,必须限流,才能保证系统不宕机. 限流算法 限流算法一般有三 ...
随机推荐
- layui laydate日期时间范围,时间默认设定为23:59:59
在Layui中,如果你想设置日期时间选择器(datetime)的默认结束时间为当天的23:59:59,你可以使用如下代码(红色部分): laydate.render({ elem: '#test10' ...
- 2022 GDOI普及组游记
2022 GDOI普及组游记 注:传送门均为校内网址 day -4 被年级主任集中开会,给我们免了亿堆作业,灌了亿壶鸡汤,宣布了为期一(亿)周的集训开始. day -3 中午一直在复习期中(4.21- ...
- 异步编程在ArkTS中具体怎么实现?
大家好,我是 V 哥,很好奇,在ArkTS中实现异步编程是怎样的,今天的内容来聊聊这个问题,总结了一些学习笔记,分享给大家,在 ArkTS中实现异步编程主要可以通过以下几种方式: 1. 使用async ...
- AI翻唱神器,一键用你喜欢的歌手翻唱他人的曲目(附下载链接)
最近,"AI孙燕姿"翻唱众多明星的歌曲在各大网络平台上走红,其作品不仅累积上千万的播放量,在科技圈和音乐圈也都引发了热议,歌手孙燕姿在社交平台发文回应称:人类无法超越AI技术已指日 ...
- C#/.NET/.NET Core技术前沿周刊 | 第 14 期(2024年11.18-11.24)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...
- PHP开源项目之YOURLS
YOURLS是一个开源的PHP的程序,可以利用它来构建属于自己的URL缩短服务,YOURLS还可以集成到WordPress博客中使用. YOURLS 的主要功能: 公开的(Public 任何人都可以用 ...
- 抓包工具之Charles(mac)
下载地址:https://www.charlesproxy.com/download/ 因为软件是收费的,所以破解方式可以参考:https://www.zzzmode.com/mytools/char ...
- Docker启动Nacos2.4.3并且使用MySQL
1.拉取容器 docker pull nacos/nacos-server:v2.4.3 2.创建宿主机挂载目录 mkdir -p /docker/nacos/logs/ mkdir -p /dock ...
- VTK 视角的旋转、平移、缩放
在CAD/CAM软件中,都需要旋转.平移和缩放视角,来观察操作图形.由于VTK定义的交互的类型不是很适用,所有通过定义一套自己的交互方式. 在下面代码中,鼠标左键平移,滚轮缩放,右键旋转. 先定义一个 ...
- 在 ASP.NET Core 中使用 Tailwind
在 ASP.NET Core 中使用 Tailwind https://khalidabuhakmeh.com/install-tailwind-css-with-aspnet-core 表单和函数是 ...