OpenResty Learning Chapter 8 Traffic Replication/AB Testing/Associations

Keywords: Java Programming Nginx REST

Links to the original text: https://blog.csdn.net/jinnianshilongnian/article/details/84705004

This article is transferred from https://blog.csdn.net/jinnianshilongnian/article/details/84705004 Thank you for sharing!

 

Traffic replication

In the actual development, project upgrade is often involved, and the upgrade can not be simply put on line, it needs to verify whether the upgrade is compatible with the old online, so it may need to run two projects in parallel for a period of time for data comparison and verification, and then go online after no problem. This actually requires traffic replication, replicating traffic to other servers, one way is to use such as tcpcopy Drainage; in addition, we can use ngx.location.capture_multi in the HTTP LuaModule module of nginx for concurrent execution to simulate replication.

 

Construct two services

    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")
        ';
    }

Called through 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
                --Recording errors
             end
         ';
    }

The underlying layer of ngx.location.capture is implemented by cosocket, which supports the protocol in Lua, through which non-blocking code can be written synchronously.

 

Here we should consider recording failure, replaying the failed data or abandoning processing according to our own business.

 

AB test

AB testing is multi-version testing. Sometimes when we develop a new version, we need grayscale testing. That is, some people can see the new version, some people can see the old version, and then decide whether to switch to the new version by accessing the data. For example, you can cut versions according to the region, user and other information.

 

For example, there is a cookie called _jda in Jingdong Mall, which is planted when users visit the website, so we can get the cookie and choose the version according to the cookie.

 

For example, two empty cookie visits found that the second digit string was changed, that is, we can judge from the second digit string.

__jda=122270672.1059377902.1425691107.1425691107.1425699059.1

__jda=122270672.556927616.1425699216.1425699216.1425699216.1.

 

Judgment rules can be more choices, such as through the tail number; to cut 30% of the traffic to the new version, you can choose the tail number of 1, 3, 5 to the new version, the rest is still in the old version.

 

1. Use map to select version

map $cookie___jda $ab_key {
    default                                       "0";
    ~^\d+\.\d+(?P<k>(1|3|5))\.                    "1";
}

Use map The mapping rule, that is, to the new version is equal to "1" and to the old version is equal to "0"; then we can get the data through 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 can also be used here on different versions of servers.

    location /abtest2 {
        if ($ab_key = "1") {
            rewrite ^ /test1 break;
            proxy_pass http://backend1;
        }
        rewrite ^ /test2 break;
        proxy_pass http://backend2;
    }

2. Use lua-resty-cookie to get the Cookie directly in Lua for parsing

First download 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
        ';
 
    }

First use lua-resty-cookie Get cookie s, then use ngx.re.match to match rules, and finally use ngx.exec or ngx.location.capture to process. The purpose of using ngx.exec and ngx.location.capture here is to demonstrate, and there is no exception handling for ngx.location.capture.

 

Association

There is no concept of thread and asynchronous programming in Lua, which provides the concept of Coordinator for concurrent execution. Personally, I think that the coordinator is to give CPU usage right to B if it finds itself busy in running A. Finally, A can continue to execute from the interrupt position, local or single thread, and CPU is exclusive; therefore, if writing network program needs to be done. It is implemented with non-blocking I/O.

 

The ngx_lua module encapsulates the coroutines. We can call the ngx.thread API directly. Although it is called "lightweight thread", its essence is the Lua coroutines. The API must be used in conjunction with the non-blocking I/O API provided by the ngx_lua module, such as ngx.location.capture_multi, lua-resty-redis, lua-resty-mysql and other cosocket-based implementations we used before.

 

Through Lua, we can call multiple interfaces concurrently, then who executes successfully and who returns first, similar to BigPipe model.

 

1. Dependent API s

    location /api1 {
        echo_sleep 3;
        echo api1 : $arg_a;
    }
    location /api2 {
        echo_sleep 3;
        echo api2 : $arg_a;
    }

We use echo_sleep to wait for three seconds.

 

2. Serial Implementation

    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))
        ';
    }

The total execution time of each call is more than 6 seconds, such as accessing http://192.168.1.2/serial?a=22.

api1 : 22 
api2 : 22 
6.0040001869202

 

3. Implementation of 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))
        ';
    }

Use ngx.location.capture_multi directly, such as accessing http://192.168.1.2/concurrency1?a=22

api1 : 22 
api2 : 22 
3.0020000934601

    

4. Implementing the Cooperative 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))
        ';
    }

Use ngx.thread.spawn Create a lightweight thread, and then use ngx.thread.wait Wait for the thread to execute successfully. For example, visit http://192.168.1.2/concurrency2?a=22

api1 : 22 
api2 : 22 
3.0030000209808

   

It is somewhat similar to the thread pool execution model in Java, but different from the thread pool, it only executes one function at a time, and when IO waits, it gives up the CPU for the next execution. We can achieve either success by returning in the following way, before waiting for all execution success to return.

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

 

Lua Co-engineering References

<Programming in Lua>

http://timyang.net/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.luaer.cn/2.11.html

Posted by kausee on Tue, 13 Aug 2019 03:02:29 -0700