流量复制

在实际开发中经常涉及到项目的升级,而该升级不能简单的上线就完事了,需要验证该升级是否兼容老的上线,因此可能需要并行运行两个项目一段时间进行数据比对和校验,待没问题后再进行上线。这其实就需要进行流量复制,把流量复制到其他服务器上,一种方式是使用如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. 问题:深度学习时代的初期最为火热的AI安全问题已经很少有人讨论了,那么是不是已经解决该问题了呢?

    答案: 先说结果,该问题并没有被解决. 之所以该问题已经不是最初的那么火热的讨论和研究热点了,其主要原因是大家发现这个神经网络在深度学习时代是十分的work的,虽然AI安全问题一直没有解决,但是比较发 ...

  2. Linux Shell简介

    目录 Shell是什么 基本介绍 用Shell编写HelloWorld Shell是什么 Shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以 ...

  3. FFmpeg转码音视频时间戳设置分析

    音频时间戳设置 以下代码基于FFmpeg n5.1.2进行分析 以下文档中有关音频的具体时间戳数据来自以下转码命令: ./ffmpeg_g -rw_timeout 5000000 -i 'rtmp:/ ...

  4. 连接数据库报错的异常可以用mysqli_report来捕获

    有时候数据库密码改了或者数据库删了,就会有一个mysqli的链接报错,是因为直接使用了类似代码 $connection = new mysqli('127.0.0.1', 'test_user', ' ...

  5. webpack中引用jQuery的四种方式

    import webpack中是根据一个入口文件开始收集依赖. import $ from 'jquery' 但是一个项目中通常有很多个地方都用到了jQuery,每个模块都要这样的一行代码 那么如何解 ...

  6. 扩展 Tomcat Web 服务器的功能

    把服务器提升到新的高度 本教程是所有 Web 服务器或应用服务器管理员的必读指南.尽管 Apache Tomcat 的实现与其他 Web 服务器略有不同,但是本教程为许多高级管理任务提供了一种符合逻辑 ...

  7. PostgreSql Docker 主从热备,异步流复制方案

    环境说明 Docker Windows 11 PostgreSql 16 方案步骤 0. 宿主机准备: 找个地方创建一个文件夹用来挂载容器中数据库Data文件夹,这里我用的是: C:\Users\Ad ...

  8. Spring Boot 使用 slf4j 进行日志记录

    SLF4J,即简单日志门面(Simple Logging Facade forJava),不是具体的日志解决方案,它只服务于各种各样的日志系统.按照官方的说法,SLF4J 是一个用于日志系统的简单Fa ...

  9. SaaS架构中多租户的概念

    SaaS架构中多租户的概念 租户可以理解为部署在云端的客户,通常出现在2B的企业中,比如现在学校的一卡通管理,通常是一个公司来做的,学校本地不需要做任何部署,而这个公司又是服务了很多个学校,那么学校对 ...

  10. 使用 Windows Debugger 调试托管代码

    使用 Windows Debugger 调试托管代码 https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugg ...