流量复制

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

构造两个服务

  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调用

  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. content_by_lua '
  9. local res1, res2 = ngx.location.capture_multi{
  10. { "/test1", { args = ngx.req.get_uri_args() } },
  11. { "/test2", { args = ngx.req.get_uri_args()} },
  12. }
  13. if res1.status == ngx.HTTP_OK then
  14. ngx.print(res1.body)
  15. end
  16. if res2.status ~= ngx.HTTP_OK then
  17. --记录错误
  18. end
  19. ';
  20. }

此处可以根据需求设置相应的超时时间和长连接连接池等;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选择版本

  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获取到该数据。

  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到不同版本的服务器上

  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

  1. cd /usr/example/lualib/resty/
  2. wget https://raw.githubusercontent.com/cloudflare/lua-resty-cookie/master/lib/resty/cookie.lua
  1. location /abtest3 {
  2. content_by_lua '
  3. local ck = require("resty.cookie")
  4. local cookie = ck:new()
  5. local ab_key = "0"
  6. local jda = cookie:get("__jda")
  7. if jda then
  8. local v = ngx.re.match(jda, [[^\d+\.\d+(1|3|5)\.]])
  9. if v then
  10. ab_key = "1"
  11. end
  12. end
  13. if ab_key == "1" then
  14. ngx.exec("/test1", ngx.var.args)
  15. else
  16. ngx.print(ngx.location.capture("/test2", {args = ngx.req.get_uri_args()}).body)
  17. end
  18. ';
  19. }

首先使用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

  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、串行实现

  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

  1. api1 : 22
  2. api2 : 22
  3. 6.0040001869202

3、ngx.location.capture_multi实现

  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. local t2 = ngx.now()
  9. ngx.print(res1.body, "<br/>", res2.body, "<br/>", tostring(t2-t1))
  10. ';
  11. }

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

  1. api1 : 22
  2. api2 : 22
  3. 3.0020000934601

4、协程API实现 

  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

  1. api1 : 22
  2. api2 : 22
  3. 3.0030000209808

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

  1. local  ok, res = ngx.thread.wait(thread1, thread2)

第八章 (Nginx+Lua)流量复制/AB测试/协程的更多相关文章

  1. 高并发 Nginx+Lua OpenResty系列(11)——流量复制/AB测试/协程

    流量复制 在实际开发中经常涉及到项目的升级,而该升级不能简单的上线就完事了,需要验证该升级是否兼容老的上线,因此可能需要并行运行两个项目一段时间进行数据比对和校验,待没问题后再进行上线.这其实就需要进 ...

  2. Lua基础之coroutine(协程)

    概括:1.创建协程2.coroutine的函数3.coroutine的基本流程4.yield对coroutine流程的干预5.resume, function()以及yield之间的参数传递和返回值传 ...

  3. lua学习笔记13:协程具体解释和举例

    一.coroutine.create创建协程 參数是协程的主函数,返回一个thread对象 co = coroutine.create(function() print("coroutine ...

  4. [lua]异步串行流程*协程

    local function param_pack( params, callback ) table.insert(params, callback) return params end local ...

  5. 跟我学OpenResty(Nginx+Lua)开发目录贴 (转)

    使用Nginx+Lua开发近一年的时间,学习和实践了一些Nginx+Lua开发的架构,为了让更多人使用Nginx+Lua架构开发,利用春节期间总结了一份基本的学习教程,希望对大家有用.也欢迎谈探讨学习 ...

  6. (zt)Lua的多任务机制——协程(coroutine)

    原帖:http://blog.csdn.net/soloist/article/details/329381 并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上 ...

  7. Lua的多任务机制——协程(coroutine)

    并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上有这么两种多任务技术,一种是抢占式多任务(preemptive multitasking),它让操作系统来决定 ...

  8. Python线程和协程CPU资源利用率测试

    前言介绍 协程 ,又称为微线程,它是实现多任务的另一种方式,只不过是比线程更小的执行单元.因为它自带CPU的上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程.通俗的理解: 在一个线程中 ...

  9. Nginx流量复制

    1. 需求 将生产环境的流量拷贝到预上线环境或测试环境,这样做有很多好处,比如: 可以验证功能是否正常,以及服务的性能: 用真实有效的流量请求去验证,又不用造数据,不影响线上正常访问: 这跟灰度发布还 ...

  10. Nginx + LUA下流量拦截算法

    前言 每逢大促必压测,每逢大促必限流,这估计是电商人的常态.每次大促期间,业务流量是平时的几倍十几倍,大促期间大部分业务都会集中在购物车结算,必须限流,才能保证系统不宕机. 限流算法 限流算法一般有三 ...

随机推荐

  1. The 2024 ICPC Asia East Continent Online Contest (I) C

    Link: Permutation Counting 4 我的评价是神题,给出两种做法. 方法一 利用线代技巧. 设法构造矩阵 \(A\), 其中 \(A_{ij} = [j \in [l_i, r_ ...

  2. docker部署java项目

    1.首先你需要提前准备好jar包或者war包,并想办法放入Linux环境(或虚拟机)中: 2.java项目的部署需要用到Tomcat或者Jetty,docker可以直接拉取他俩的镜像,这里以Tomca ...

  3. .NET9 EFcore支持早期MSSQL数据库 ROW_NUMBER()分页

    前言 NET程序员是很幸福的,MS在上个月发布了NET9.0RTM,带来了不少的新特性,但是呢,我们是不是还有很多同学软硬件都还没更上,比如,自己的电脑还在跑Win7,公司服务器还在跑MSSQL200 ...

  4. python项目依赖管理之poetry

    poetry,是一个强大的Python项目依赖管理工具,旨在简化和优化项目的依赖管理过程.它提供了一种简单且一致的方式来定义.安装和管理项目所需的依赖项.本文将详细介绍poetry库的安装方法.使用方 ...

  5. golang之性能分析工具pprof

    PProf 是一个 Go 程序性能分析工具,可以分析 CPU.内存等性能.Go 在语言层面上集成了 profile 采样工具,只需在代码中简单地引入 runtime/ppro 或者 net/http/ ...

  6. 基于python的文件监控watchdog

    实时监控第三方库watchdog,其原理通过操作系统的时间触发的,不需要循环和等待 使用场景: 1.监控文件系统中文件或目录的增删改情况 2.当特定的文件被创建,删除,修改,移动时执行相应的任务 1. ...

  7. vagrant搭建开发环境

    一:我们为什么需要用这玩意 我们在开发中经常会面临的问题:环境不一致,有人用Mac有人用Windos还有几个用linux的,而我们的服务器都是linux. 在我本地是可以的啊,我测了都,没有问题啊,然 ...

  8. Mybatis【6】-- Mybatis插入数据后自增id怎么获取?

    代码直接放在Github仓库[https://github.com/Damaer/Mybatis-Learning/tree/master/mybatis-05-CURD ] 需要声明的是:此Myba ...

  9. [转载] 6 个技巧,提升 C++11 的 vector 性能

    转载:https://www.sohu.com/a/120595688_465979 Vector 就像是 C++ STL 容器的瑞士军刀.Bjarne Stoutsoup 有一句话 – " ...

  10. 从底层源码深入分析Spring的IoC容器初始化过程

    IOC容器的初始化整体过程 Spring是如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的?这主要会经过以下 4 步: 从XML中读取配置文 ...