小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

第八章 流量復(fù)制/AB測試/協(xié)程

 昵稱597197 2018-01-10

流量復(fù)制

在實(shí)際開發(fā)中經(jīng)常涉及到項(xiàng)目的升級,而該升級不能簡單的上線就完事了,需要驗(yàn)證該升級是否兼容老的上線,因此可能需要并行運(yùn)行兩個項(xiàng)目一段時間進(jìn)行數(shù)據(jù)比對和校驗(yàn),待沒問題后再進(jìn)行上線。這其實(shí)就需要進(jìn)行流量復(fù)制,把流量復(fù)制到其他服務(wù)器上,一種方式是使用如tcpcopy引流;另外我們還可以使用nginx的HttpLuaModule模塊中的ngx.location.capture_multi進(jìn)行并發(fā)執(zhí)行來模擬復(fù)制。

 

構(gòu)造兩個服務(wù)

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調(diào)用

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. }  

此處可以根據(jù)需求設(shè)置相應(yīng)的超時時間和長連接連接池等;ngx.location.capture底層通過cosocket實(shí)現(xiàn),而其支持Lua中的協(xié)程,通過它可以以同步的方式寫非阻塞的代碼實(shí)現(xiàn)。

 

此處要考慮記錄失敗的情況,對失敗的數(shù)據(jù)進(jìn)行重放還是放棄根據(jù)自己業(yè)務(wù)做處理。

 

AB測試

AB測試即多版本測試,有時候我們開發(fā)了新版本需要灰度測試,即讓一部分人看到新版,一部分人看到老版,然后通過訪問數(shù)據(jù)決定是否切換到新版。比如可以通過根據(jù)區(qū)域、用戶等信息進(jìn)行切版本。

 

比如京東商城有一個cookie叫做__jda,該cookie是在用戶訪問網(wǎng)站時種下的,因此我們可以拿到這個cookie,根據(jù)這個cookie進(jìn)行版本選擇。

 

比如兩次清空cookie訪問發(fā)現(xiàn)第二個數(shù)字串是變化的,即我們可以根據(jù)第二個數(shù)字串進(jìn)行判斷。

__jda=122270672.1059377902.1425691107.1425691107.1425699059.1

__jda=122270672.556927616.1425699216.1425699216.1425699216.1。

 

判斷規(guī)則可以比較多的選擇,比如通過尾號;要切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映射規(guī)則,即如果是到新版則等于"1",到老版等于“0”; 然后我們就可以通過ngx.var.ab_key獲取到該數(shù)據(jù)。

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到不同版本的服務(wù)器上 

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進(jìn)行解析

首先下載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進(jìn)行規(guī)則的匹配,最后使用ngx.exec或者ngx.location.capture進(jìn)行處理。此處同時使用ngx.exec和ngx.location.capture目的是為了演示,此外沒有對ngx.location.capture進(jìn)行異常處理。

 

協(xié)程

Lua中沒有線程和異步編程編程的概念,對于并發(fā)執(zhí)行提供了協(xié)程的概念,個人認(rèn)為協(xié)程是在A運(yùn)行中發(fā)現(xiàn)自己忙則把CPU使用權(quán)讓出來給B使用,最后A能從中斷位置繼續(xù)執(zhí)行,本地還是單線程,CPU獨(dú)占的;因此如果寫網(wǎng)絡(luò)程序需要配合非阻塞I/O來實(shí)現(xiàn)。

 

ngx_lua 模塊對協(xié)程做了封裝,我們可以直接調(diào)用ngx.thread API使用,雖然稱其為“輕量級線程”,但其本質(zhì)還是Lua協(xié)程。該API必須配合該ngx_lua模塊提供的非阻塞I/O API一起使用,比如我們之前使用的ngx.location.capture_multi和lua-resty-redis、lua-resty-mysql等基于cosocket實(shí)現(xiàn)的都是支持的。

 

通過Lua協(xié)程我們可以并發(fā)的調(diào)用多個接口,然后誰先執(zhí)行成功誰先返回,類似于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、串行實(shí)現(xiàn)

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. }  

即一個個的調(diào)用,總的執(zhí)行時間在6秒以上,比如訪問http://192.168.1.2/serial?a=22

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

 

3、ngx.location.capture_multi實(shí)現(xiàn)

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來實(shí)現(xiàn),比如訪問http://192.168.1.2/concurrency1?a=22

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

    

4、協(xié)程API實(shí)現(xiàn) 

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創(chuàng)建一個輕量級線程,然后使用ngx.thread.wait等待該線程的執(zhí)行成功。比如訪問http://192.168.1.2/concurrency2?a=22

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

   

其有點(diǎn)類似于Java中的線程池執(zhí)行模型,但不同于線程池,其每次只執(zhí)行一個函數(shù),遇到IO等待則讓出CPU讓下一個執(zhí)行。我們可以通過下面的方式實(shí)現(xiàn)任意一個成功即返回,之前的是等待所有執(zhí)行成功才返回。

Java代碼  收藏代碼
  1. local  ok, res = ngx.thread.wait(thread1, thread2)  

 

Lua協(xié)程參考資料

《Programming in Lua》

http:///lua/lua-coroutine-vs-java-wait-notify/

https://github.com/andycai/luaprimer/blob/master/05.md

http://my.oschina.net/wangxuanyihaha/blog/186401

http://manual./2.11.html

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多