ngx_lua中提供了ngx.socket API,能夠方便的訪問第三方網絡服務。以下面的代碼,經過get_response函數從兩個(或者更多)的源服務器獲取數據,再生成響應發給客戶端。nginx
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就是用來解決這種問題的。git
lua-nginx-module提供了三個APIgithub
關於API的介紹能夠參考官方文檔。shell
經過ngx.thread.spawn能夠生成一個"light thread",一個」light thread「和Lua的協程相似,區別在於"light thread"是由ngx_lua模塊進行調度的,多個"light thread"同時運行。服務器
"light thread"比Lua中的協程更像操做系統中的進程。網絡
用ngx.thread重寫上面的代碼socket
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"能夠同時運行,總的耗時只至關於訪問一個源服務器的時間,即便須要訪問的源服務器增長,耗時沒有太大的變化。tcp
Linux中的fork生成新的子進程,父進程與子進程誰先運行呢?都有可能,和系統的調度有關。函數
把調用ngx.thread.spawn的這個Lua協程稱爲父協程,生成的"light thread"和父協程誰先運行呢? 在ngx_lua的調度邏輯中,是生成的"light thread"先運行,運行結束或者被掛起後,父協程纔會繼續運行。實際的代碼在ngx_http_lua_run_thread函數中,這個函數比較複雜,涉及的東西太多,稍後再細說。lua
"light thread"畢竟是基於依附於請求的,如在content_by_lua中建立的"light thread",是徹底與當前的請求關聯的,若是"light thread"沒有退出,當前請求也沒法結束。一樣若是當前請求由於錯誤退出,或調用ngx.exit強制退出時,處於運行狀態的"light thread"也會被kill掉。不像操做系統的進程,父進程退出後,子進程能夠被init進程"收養"。
以下面的代碼,沒有調用ngx.thread.wait去等待"light thread"的結束。
location / { 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/03/02 22:43:21 [error] 2142#0: *1 [lua] content_by_lua(nginx.conf:55):3: thread name: first, now start, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:43:21 [error] 2142#0: *1 [lua] content_by_lua(nginx.conf:55):3: thread name: second, now start, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:43:21 [error] 2142#0: *1 [lua] content_by_lua(nginx.conf:55):10: main thread end, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:43:25 [error] 2142#0: *1 [lua] content_by_lua(nginx.conf:55):5: thread name: first, now end, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:43:25 [error] 2142#0: *1 [lua] content_by_lua(nginx.conf:55):5: thread name: second, now end, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1"
而若是代碼中主動調用了ngx.exit()結束請求,那麼t1,t2兩個沒有打印出徹底的信息就被kill掉了。
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) ngx.exit(100) } }
相應的Nginx日誌
2017/03/02 22:48:01 [error] 2227#0: *1 [lua] content_by_lua(nginx.conf:56):3: thread name: first, now start, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:48:01 [error] 2227#0: *1 [lua] content_by_lua(nginx.conf:56):3: thread name: second, now start, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:48:01 [error] 2227#0: *1 [lua] content_by_lua(nginx.conf:56):10: main thread end, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:48:35 [error] 2227#0: *2 [lua] content_by_lua(nginx.conf:56):3: thread name: first, now start, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:48:35 [error] 2227#0: *2 [lua] content_by_lua(nginx.conf:56):3: thread name: second, now start, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1" 2017/03/02 22:48:35 [error] 2227#0: *2 [lua] content_by_lua(nginx.conf:56):10: main thread end, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "127.0.0.1"