ngx_lua_API 指令詳解(六)ngx.thread.spawn、ngx.thread.wait、ngx.thread.kill介紹


摘要:通過lua-nginx-module中的ngx.thread同時執行多個任務。

ngx_lua中訪問多個第三方服務

ngx_lua中提供了ngx.socket API,可以方便的訪問第三方網絡服務。如下面的代碼,通過get_response函數從兩個(或者更多)的源服務器獲取數據,再生成響應發給客戶端。

location / { content_by_lua_block { local get_response(host, port) local sock = ngx.socket.tcp() local ok, err = sock:connect(host, port) if not ok then return nil, err end local data, err = sock:receive() if not data then return nil, err end return data end local first = get_response("lua.org", 8080) local second = get_response("nginx.org", 8080) ngx.say(first .. second) } }

如果需要10個第三方網絡服務,需要調用get_response 10次。總的響應時間與需要連接源的數量成正比。那么如何縮短源的響應時間呢?ngx.thread就是用來解決這種問題的。

二、lua-nginx-module提供了三個API

  1、ngx.thread.spawn

  2、ngx.thread.wait

  3、ngx.thread.kill

三、詳解

1、ngx.thread.spawn

重點:ngx.thread.spawn生成新的"light thread",這個"light thread"運行優先級比它的父協程高,會優先運行,父協程被迫暫停。"light thread"運行結束或者yield后,再由ngx_http_lua_run_posted_threads去運行父協程。

參考:ngx_lua中的協程調度(六)之ngx_http_lua_run_posted_thread

通過ngx.thread.spawn可以生成一個"light thread",一個”light thread“和Lua的協程類似,區別在於"light thread"是由ngx_lua模塊進行調度的,多個"light thread"同時運行。

"light thread",協程 和 進程。"light thread"比Lua中的協程更像操作系統中的進程。

  • fork生成新的進程,生成的多個進程可以同時運行,而ngx.thread.spawn生成新的協程,多個協程同時在跑。
  • kill可以殺死不需要的子進程,ngx.thread.kill可以殺死不需要的"light thread"
  • wait可以等待子進程結束並取得子進程退出狀態,ngx.thread.wait可以等待"light thread"結束並獲取其返回值。

ngx.thread的使用,用ngx.thread重寫上面的代碼

location / { content_by_lua_block { local get_response(host, port) local sock = ngx.socket.tcp() local ok, err = sock:connect(host, port) if not ok then return nil, err end local data, err = sock:receive() if not data then return nil, err end return data end local t1 = ngx.thread.spawn(get_response, "lua.org", 8080) local t2 = ngx.thread.spawn(get_response, "nginx.org", 8080) local ok, res1, res2 = ngx.thread.wait(t1, t2) ngx.say(res1 .. res2) } }

生成的兩個"light thread"可以同時運行,總的耗時只相當於訪問一個源服務器的時間,即使需要訪問的源服務器增加,耗時沒有太大的變化。

"light thread"的調度

  Linux中的fork生成新的子進程,父進程與子進程誰先運行呢?都有可能,和系統的調度有關。

  把調用ngx.thread.spawn的這個Lua協程稱為父協程,生成的"light thread"和父協程誰先運行呢? 在ngx_lua的調度邏輯中,是生成的"light thread"先運行,運行結束或者被掛起后,父協程才會繼續運行。實際的代碼在ngx_http_lua_run_thread函數中,這個函數比較復雜,涉及的東西太多,稍后再細說。

如下面的代碼,沒有調用ngx.thread.wait去等待"light thread"的結束。

# [1] 沒有調用ngx.thread.wait去等待"light thread"的結束 location /thread002 { content_by_lua_block { local function f(name) ngx.log(ngx.ERR, "thread name: ", name, ", now start") ngx.sleep(4) ngx.log(ngx.ERR, "thread name: ", name, ", now end") end local t1 = ngx.thread.spawn(f, "first") local t2 = ngx.thread.spawn(f, "second") ngx.log(ngx.ERR, "main thread end") } }

由Nginx的日志中可以看到當前的請求一直延遲到t1,t2兩個"light thread"最后退出才會結束。 Nginx中日志的順序也可以看出父協程和兩個"light thread"的執行那個順序。

2017/07/21 09:45:07 [error] 115387#0: *26 [lua] content_by_lua(thread.conf:140):3: thread name: first, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
2017/07/21 09:45:07 [error] 115387#0: *26 [lua] content_by_lua(thread.conf:140):3: thread name: second, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
2017/07/21 09:45:07 [error] 115387#0: *26 [lua] content_by_lua(thread.conf:140):10: main thread end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
2017/07/21 09:45:11 [error] 115387#0: *26 [lua] content_by_lua(thread.conf:140):5: thread name: first, now end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
2017/07/21 09:45:11 [error] 115387#0: *26 [lua] content_by_lua(thread.conf:140):5: thread name: second, now end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"

而如果代碼中主動調用了ngx.exit()結束請求,那么t1,t2兩個沒有打印出完全的信息就被kill掉了。

# [2] 代碼中主動調用了ngx.exit()結束請求,那么t1,t2兩個沒有打印出完全的信息就被kill掉了 location /thread003 { content_by_lua_block { local function f(name) ngx.log(ngx.ERR, "thread name: ", name, ", now start") ngx.sleep(4) ngx.log(ngx.ERR, "thread name: ", name, ", now end") end local t1 = ngx.thread.spawn(f, "first") local t2 = ngx.thread.spawn(f, "second") ngx.exit(200) } }

相應的Nginx日志

2017/07/21 09:48:08 [error] 115508#0: *28 [lua] content_by_lua(thread.conf:156):3: thread name: first, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread003 HTTP/1.1", host: "127.0.0.1:8689"
2017/07/21 09:48:08 [error] 115508#0: *28 [lua] content_by_lua(thread.conf:156):3: thread name: second, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread003 HTTP/1.1", host: "127.0.0.1:8689"
2017/07/21 09:48:08 [error] 115508#0: *28 [lua] content_by_lua(thread.conf:156):10: main thread end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread003 HTTP/1.1", host: "127.0.0.1:8689"

"light thread"的限制

  "light thread"畢竟是基於依附於請求的,如在content_by_lua中創建的"light thread",是完全與當前的請求關聯的,如果"light thread"沒有退出,當前請求也無法結束。同樣如果當前請求因為錯誤退出,或調用ngx.exit強制退出時,處於運行狀態的"light thread"也會被kill掉。不像操作系統的進程,父進程退出后,子進程可以被init進程"收養"。

錯誤代碼:

2017/07/21 09:14:10 [error] 114598#0: *1 failed to load inlined Lua code: content_by_lua(thread.conf:128):2: ')' expected near ',', client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread001 HTTP/1.1", host: "127.0.0.1:8689"

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM