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
http://timyang.net/lua/lua-coroutine-vs-java-wait-notify/
https://github.com/andycai/luaprimer/blob/master/05.md