Nginx模塊Lua-Nginx-Module學習筆記(二)Lua指令詳解(Directives)

源碼地址:https://github.com/Tinywan/Lua-Nginx-Redis

Nginx與Lua編寫腳本的基本構建塊是指令。 指令用於指定什麼時候運行用戶Lua代碼以及如何使用結果。 下面是顯示指令執行順序的圖。html

 當一個請求發起一個「子請求」的時候,按照 Nginx 的術語,習慣把前者稱爲後者的「父請求」(parent request)。linux

location /main { echo_location /foo;     # echo_location發送子請求到指定的location 
    echo_location /bar; } location /foo { echo Tinywan_foo; } location /bar { echo Tinywan_bar; } 

重啓Nginx,curl訪問nginx

root@iZ236j3sofdZ:/usr/local/nginx/conf # service nginx restart
 * Stopping Nginx Server... [ OK ] * Starting Nginx Server... [ OK ] root@iZ236j3sofdZ:/usr/local/nginx/conf # curl 'http://localhost/main'
Tinywan_foo Tinywan_bar

  這裏,main location就是發送2個子請求,分別到foo和bar,這就相似一種函數調用。  「子請求」方式的通訊是在同一個虛擬主機內部進行的,因此 Nginx 核心在實現「子請求」的時候,就只調用了若干個 C 函數,徹底不涉及任何網絡或者 UNIX 套接字(socket)通訊。咱們由此能夠看出「子請求」的執行效率是極高的。git

協程(Coroutine)

協程相似一種多線程,與多線程的區別有: github

1. 協程並不是os線程,因此建立、切換開銷比線程相對要小。 web

2. 協程與線程同樣有本身的棧、局部變量等,可是協程的棧是在用戶進程空間模擬的,因此建立、切換開銷很小。正則表達式

3. 多線程程序是多個線程併發執行,也就是說在一瞬間有多個控制流在執行。而協程強調的是一種多個協程間協做的關係,只有當一個協程主動放棄執行權,另外一個協程才能得到執行權,因此在某一瞬間,多個協程間只有一個在運行。 redis

4. 因爲多個協程時只有一個在運行,因此對於臨界區的訪問不須要加鎖,而多線程的狀況則必須加鎖。 算法

5. 多線程程序因爲有多個控制流,因此程序的行爲不可控,而多個協程的執行是由開發者定義的因此是可控的。 json

Nginx的每一個Worker進程都是在epoll或kqueue這樣的事件模型之上,封裝成協程,每一個請求都有一個協程進行處理。這正好與Lua內建協程的模型是一致的,因此即便ngx_lua須要執行Lua,相對C有必定的開銷,但依然能保證高併發能力。

原理介紹

  原理:ngx_lua將Lua嵌入Nginx,可讓Nginx執行Lua腳本,而且高併發、非阻塞的處理各類請求。Lua內建協程,這樣就能夠很好的將異步回調轉換成順序調用的形式。ngx_lua在Lua中進行的IO操做都會委託給Nginx的事件模型,從而實現非阻塞調用。開發者能夠採用串行的方式編寫程序,ngx_lua會自動的在進行阻塞的IO操做時中斷,保存上下文;而後將IO操做委託給Nginx事件處理機制,在IO操做完成後,ngx_lua會恢復上下文,程序繼續執行,這些操做都是對用戶程序透明的。 每一個NginxWorker進程持有一個Lua解釋器或者LuaJIT實例,被這個Worker處理的全部請求共享這個實例。每一個請求的Context會被Lua輕量級的協程分割,從而保證各個請求是獨立的。 ngx_lua採用「one-coroutine-per-request」的處理模型,對於每一個用戶請求,ngx_lua會喚醒一個協程用於執行用戶代碼處理請求,當請求處理完成這個協程會被銷燬。每一個協程都有一個獨立的全局環境(變量空間),繼承於全局共享的、只讀的「comman data」。因此,被用戶代碼注入全局空間的任何變量都不會影響其餘請求的處理,而且這些變量在請求處理完成後會被釋放,這樣就保證全部的用戶代碼都運行在一個「sandbox」(沙箱),這個沙箱與請求具備相同的生命週期。 得益於Lua協程的支持,ngx_lua在處理10000個併發請求時只須要不多的內存。根據測試,ngx_lua處理每一個請求只須要2KB的內存,若是使用LuaJIT則會更少。因此ngx_lua很是適合用於實現可擴展的、高併發的服務。

Nginx Lua模塊指令 


lua_code_cache

語法:  lua_code_cache on | off

默認值: lua_code_cache on

上下文:http, server, location, location if

啓用或禁用指令中Lua代碼的Lua代碼緩存*_by_lua_file(如set_by_lua_file和 content_by_lua_file)和Lua模塊,

關閉時,ngx_lua提供的每一個請求都將在一個單獨的Lua VM實例中運行,從該0.9.3版本開始。所以,set_by_lua_filecontent_by_lua_fileaccess_by_lua_file引用的Lua文件將不被緩存,全部使用的Lua模塊都將從頭開始加載。有了這個,開發人員能夠採用編輯和刷新方式。

可是請注意,編輯內聯中的Lua代碼時,在nginx.conf中編寫的Lua代碼,如set_by_luacontent_by_lua, access_by_luarewrite_by_lua指定的Lua代碼將不會被更新,nginx.conf由於只有Nginx配置文件解析器能夠正確解析該nginx.conf 文件和惟一的方式是經過發送HUP信號或僅從新啓動Nginx 來從新加載配置文件。

啓用代碼緩存即便,這是由裝載Lua的文件dofile或者loadfile 在* _by_lua_file不能被緩存(除非你緩存結果你本身)。一般,您可使用init_by_lua 或init_by_lua_file指令加載全部這些文件,也可使這些Lua文件成爲真正的Lua模塊並經過它們加載require

ngx_lua模塊不支持statApache mod_lua模塊可用的模式(還沒有)。

禁止使用Lua代碼緩存,對於生產使用是很是不鼓勵的,只能在開發過程當中使用,由於它對總體性能有顯着的負面影響。例如,在禁用Lua代碼緩存後,「hello world」Lua示例的性能可能會降低一個數量級。

lua_regex_cache_max_entries

語法:lua_regex_cache_max_entries <num>

默認值:lua_regex_cache_max_entries 1024

上下文:http

指定在工做進程級編譯的正則表達式高速緩存中容許的最大條目數。

若是指定了正則表達式選項o(即編譯一次的標誌),則ngx.re.match,ngx.re.gmatch,ngx.re.sub和ngx.re.gsub中使用的正則表達式將緩存在此緩存中。

容許的默認條目數爲1024,當達到此限制時,新的正則表達式將不被緩存(就好像未指定o選項),而且在error.log文件中將只有一個,只有一個警告:

2011/08/27 23:18:26 [warn] 31997#0:* 1 lua超過正則表達式緩存最大條目(1024),...
若是經過加載resty.core.regex模塊(或resty.core模塊)來使用lua-resty-core的ngx.re. *實現,則在此使用的正則表達式緩存使用LRU緩存。

不要爲正在生成的正則表達式(和/或替換ngx.re.sub和ngx.re.gsub的字符串參數)激活o選項,併產生無限變化以免達到指定的限制。

init_by_lua

語法:init_by_lua <lua-script-str>

上下文:http

phase:loading-config

警告自從v0.9.17發行版以來,不鼓勵使用此指令; 請改用新的init_by_lua_block指令。

當Nginx主進程(若是有的話)加載Nginx配置文件時,運行全局Lua VM級別上的參數<lua-script-str>指定的Lua代碼。

當Nginx收到HUP信號並開始從新加載配置文件時,Lua VM也將被從新建立,而且init_by_lua將在新的Lua VM上再次運行。 若是lua_code_cache指令關閉(默認爲on),則init_by_lua處理程序將在每一個請求上運行,由於在此特殊模式下,始終爲每一個請求建立獨立的Lua VM。

一般能夠經過這個鉤子註冊(true)Lua全局變量或在服務器啓動時預加載Lua模塊。 如下是預先加載Lua模塊的示例:

init_by_lua 'cjson = require "cjson"'; server { listen 80; server_name 127.0.0.1; charset utf8; default_type text/html; location = /api { content_by_lua_block { ngx.say(cjson.encode({name = 'tinywan', age = 24})) } } }

訪問輸出結果:

您也能夠在此階段初始化lua_shared_dict shm存儲。 這是一個例子:

# 定義一個字典 lua_shared_dict fruit 1m; init_by_lua_block{ local fruit = ngx.shared.fruit; fruit:set("apple", 88) } server { listen 80; server_name 127.0.0.1; charset utf8; default_type text/html; location = /api2 { content_by_lua_block { local fruit = ngx.shared.fruit; ngx.say(fruit:get("apple")) } } }

訪問輸出結果: 

但請注意,lua_shared_dict的shm存儲將不會經過配置從新加載(例如經過HUP信號)來清除。因此若是在這種狀況下不想在init_by_lua代碼中從新初始化shm存儲,那麼您只須要在shm存儲中設置一個自定義標誌,並始終檢查init_by_lua代碼中的標誌。

由於在這個上下文中的Lua代碼運行在Nginx爲其 worker 進程(若是有的話)分配以前,這裏加載的數據或代碼將享受許多操做系統在全部 worker 進程之間提供的複製(COW)功能,從而節省了不少記憶

在這種狀況下不要初始化您本身的Lua全局變量,由於使用Lua全局變量具備性能損失,並可能致使全局命名空間污染(有關更多詳細信息,請參閱Lua Variable Scope部分)。推薦的方法是使用適當的Lua模塊文件(可是不要使用標準的Lua函數模塊()來定義Lua模塊,由於它也會污染全局命名空間),並調用require()將您本身的模塊文件加載到init_by_lua或其餘上下文(require())在Lua註冊表中的全局package.loaded表中緩存加載的Lua模塊,所以您的模塊將僅爲整個Lua VM實例加載一次)。

在這種狀況下,僅支持一小部分用於Lua的Nginx API

日誌API:ngx.logprint
共享字典API:ngx.shared.DICT
在將來的用戶請求的狀況下,能夠支持更多用於Lua的Nginx API。

基本上,您能夠安全地使用在這種狀況下阻止I / O的Lua庫,由於在服務器啓動期間阻止主進程徹底正常。即便Nginx內核在配置加載階段也阻止I / O(至少在解析上游的主機名稱)。

您應該很是當心您在此上下文註冊的Lua代碼中的潛在安全漏洞,由於Nginx主進程一般在root賬戶下運行。

該指令首先在v0.5.5版本中引入。

/dev/shm/是linux下一個很是有用的目錄,由於這個目錄不在硬盤上,而是在內存裏。所以在linux下,就不須要大費周折去建ramdisk,直接使用/dev/shm/就可達到很好的優化效果。

在linux下,它默認最大爲內存的一半大小,使用df -h命令能夠看到

參考:Linux目錄下/dev/shm的理解和使用

init_by_lua_block

 init_by_lua_block { print("I need no extra escaping here, for example: \r\nblah") }

init_by_lua_file

init_by_lua_file "/Lua/lua_project_v0.01/application/demo/cjson.lua";

init_worker_by_lua

語法:init_worker_by_lua <lua-script-str>

上下文:http

階段:starting-worker

警告自從v0.9.17發行版以來,不鼓勵使用此指令; 請改用新的init_worker_by_lua_block指令。

在啓動主進程時,在每一個Nginx工做進程的啓動時運行指定的Lua代碼。 當主進程被禁用時,該鉤子將在init_by_lua *以後運行。

這個鉤子一般用於建立每一個工做者重複的定時器(經過ngx.timer.at Lua API),用於後端健康檢查或其餘定時平常工做。 如下是一個例子,

 init_worker_by_lua '      local delay = 3  -- in seconds local new_timer = ngx.timer.at local log = ngx.log local ERR = ngx.ERR local check check = function(premature) if not premature then
             -- do the health check or other routine work local ok, err = new_timer(delay, check) if not ok then log(ERR, "failed to create timer: ", err) return end end end local ok, err = new_timer(delay, check) if not ok then log(ERR, "failed to create timer: ", err) return end ';

init_worker_by_lua_block

語法:init_worker_by_lua_block {lua-script}

上下文:http

階段:起始人

與init_worker_by_lua指令相似,除了該僞指令直接在一對花括號({})中內聯Lua源,而不是在NGINX字符串文字中(須要特殊字符轉義)。例如:

 lua_shared_dict healthcheck 1m; lua_socket_log_errors off; init_worker_by_lua_block { local hc = require "resty.upstream.healthcheck" local ok, err = hc.spawn_checker{ shm = "healthcheck", upstream = "websocket_proxy", type = "http", http_req = "GET /health.txt HTTP/1.0\r\nHost: websocket_proxy\r\n\r\n", interval = 2000, timeout = 1000, fall = 3, rise = 2, valid_statuses = {200, 302}, concurrency = 10, } local ok, err = hc.spawn_checker{ shm = "healthcheck", upstream = "workerman_proxy", type = "http", http_req = "GET /health.txt HTTP/1.0\r\nHost: workerman_proxy\r\n\r\n", interval = 2000, timeout = 1000, fall = 3, rise = 2, valid_statuses = {200, 302}, concurrency = 10, } }

以上爲一個後臺健康狀態的檢查,詳細配置https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-upstream-healthcheck.md

set_by_lua

語法:set_by_lua $ res <lua-script-str> [$ arg1 $ arg2 ...]

上下文:服務器,服務器if,位置,位置if

階段:重寫

警告自從v0.9.17發行版以來,不鼓勵使用此指令;請改用新的set_by_lua_block指令。

使用可選的輸入參數$ arg1 $ arg2 ...執行<lua-script-str>中指定的代碼,並將字符串輸出返回給$ res。 <lua-script-str>中的代碼能夠進行API調用,並能夠從ngx.arg表中檢索輸入參數(索引從1開始,依次增長)。

該指令旨在執行短,快速運行的代碼塊,由於在代碼執行期間Nginx事件循環被阻止。所以應避免耗時的代碼序列。

該指令經過將自定義命令注入到標準ngx_http_rewrite_module的命令列表中來實現。由於ngx_http_rewrite_module在其命令中不支持非阻塞I / O,所以須要產生當前Lua「light thread」的Lua API在此指令中沒法工做。

至少如下API功能目前在set_by_lua的上下文中被禁用:

輸出API函數(例如,ngx.say ngx.send_headers
控制API函數(例如,ngx.exit
子請求API函數(例如,ngx.location.capturengx.location.capture_multi
Cosocket API函數(例如,ngx.socket.tcpngx.req.socket)。
睡眠API函數ngx.sleep。
另外,請注意,這個指令一次只能寫出一個Nginx變量的值。可是,可使用ngx.var.VARIABLE接口進行解決。

location /set_by_lua_test { set $diff ''; # we have to predefine the $diff variable here set_by_lua $sum '         local a = 32 local b = 56 ngx.var.diff = a - b;  -- write to $diff directly return a + b;          -- return the $sum value normally ';
    echo "sum = $sum, diff = $diff"; }

測試結果:

set_by_lua_file

語法:set_by_lua_file $res <path-to-lua-script-file> [$arg1 $arg2 ...]

上下文: server, server if, location, location if

做用時期: 重寫(rewrite

在lua代碼中能夠實現全部複雜的邏輯,可是要執行速度很快,不要阻塞.

等同於set_by_lua,除了指定的文件<path-to-lua-script-file>包含Lua代碼,或者從v0.5.0rc32發行版開始,要執行的Lua / LuaJIT字節碼。在該僞指令的<path-to-lua-script-file>參數字符串中支持Nginx可變插值。可是必須特別注意注射攻擊。

foo/bar.lua給定一個相對路徑時,在啓動Nginx服務器時,它們將被轉換爲相對於server prefix-p PATH命令行選項肯定的路徑的絕對路徑。當Lua代碼緩存打開時(默認狀況下),用戶代碼在第一次請求時被加載一次並被緩存,而且每次修改Lua源文件時必須從新加載Nginx配置。Lua代碼緩存能夠在開發期間經過切換lua_code_cache 暫時禁用offnginx.conf以免從新加載Nginx。此指令須要ngx_devel_kit模塊。

location =/lua_set_args { default_type 'text/html'; set_by_lua_file $num /usr/local/nginx/conf/lua_set_1.lua; echo $num; }

lua_set_1.lua 添加如下內容:

local uri_args = ngx.req.get_uri_args() local i = uri_args["i"] or 0 local j = uri_args["j"] or 0
return i + j

測試結果:

curl 'http://localhost/lua_set_args?i=2&j=10'
12

content_by_lua

語法: content_by_lua <lua-script-str>

上下文: location, location if

做用時期: 上下文內容

注:這個指令的使用氣餒如下v0.9.17版本。請改用content_by_lua_block指令。

充當「內容處理程序」並執行<lua-script-str>每一個請求中指定的Lua代碼字符串。Lua代碼能夠進行API調用,而且做爲獨立全局環境(即沙箱)中的新生成的協同程序來執行。不要在同一位置使用此指令和其餘內容處理程序指令。例如,此僞指令和proxy_pass僞指令不該在同一位置使用。

nginx.conf配置:

lua_package_path "/usr/local/nginx/lua/?.lua;;"; #lua 模塊 #include lua.conf; #單獨lua配置 server { listen 80; server_name localhost; location =/lua { content_by_lua '             ngx.say("Hello Lua!") ';
 } }

說明:#lua模塊路徑,多個之間」;」分隔,其中」;;」表示默認搜索路徑,默認到/usr/local/nginx下找

輸出結果:

root@iZ236j3sofdZ:/usr/local/nginx/conf # curl 'http://localhost/lua'
Hello Lua!

rewrite_by_lua_file

語法:  rewrite_by_lua_file <path-to-lua-script-file>

上下文:http, server, location, location if

做用時期: 上下文內容

做用:執行內部URL重寫或者外部重定向,典型的如僞靜態化的URL重寫。其默認執行在rewrite處理階段的最後。

概述:

  至關於rewrite_by_lua,除了指定的文件<path-to-lua-script-file>包含Lua代碼,或者從v0.5.0rc32發行版開始,要執行的Lua / LuaJIT字節碼

Nginx變量能夠在<path-to-lua-script-file>字符串中使用以提供靈活性。但這有一些風險,一般不推薦。

foo/bar.lua給定一個相對路徑時,在啓動Nginx服務器時,它們將被轉換爲相對於server prefix-p PATH命令行選項肯定的路徑的絕對路徑。

當Lua代碼緩存打開時(默認狀況下),用戶代碼在第一次請求時被加載一次並被緩存,而且每次修改Lua源文件時必須從新加載Nginx配置。Lua代碼緩存能夠在開發期間經過切換lua_code_cache 暫時禁用offnginx.conf以免從新加載Nginx。

rewrite_by_lua_file代碼將老是在結束時運行rewrite,除非請求處理相rewrite_by_lua_no_postpone被接通。

動態分派的文件路徑支持Nginx變量,就像content_by_lua_file中同樣

Example # 1

location /rewrite_by_lua_file { default_type "text/html"; rewrite_by_lua_file /usr/local/nginx/conf/lua/test_rewrite_1.lua; echo "no rewrite"; }

test_rewrite_1.lua 添加一下內容:

if ngx.req.get_uri_args()["jump"] == "1" then return ngx.redirect("http://www.jd.com?jump=1", 302) end  

當咱們請求http://192.168.1.2/lua_rewrite_1時發現沒有跳轉,

而請求http://192.168.1.2/lua_rewrite_1?jump=1時發現跳轉到京東首頁了。 此處須要301/302跳轉根據本身需求定義。

Example # 2

location /lua_rewrite_3 { default_type "text/html"; rewrite_by_lua_file /usr/local/nginx/conf/lua/test_rewrite_3.lua; echo "rewrite3 uri : $uri"; }

test_rewrite_3.lua 添加一下內容:

if ngx.req.get_uri_args()["jump"] == "1" then ngx.req.set_uri("/lua_rewrite_4", true); ngx.log(ngx.ERR, "=========") ngx.req.set_uri_args({a = 1, b = 2}); end 

ngx.req.set_uri(uri, true):能夠內部重寫uri,即會發起新的匹配location請求,等價於 rewrite ^ /lua_rewrite_4 last;此處看error log是看不到咱們記錄的log

因此請求如http://localhost/lua_rewrite_3?jump=1會到新的location中獲得響應,此處沒有/lua_rewrite_4,因此匹配到/lua請求,獲得相似以下的響應

root@iZ236j3sofdZ:/usr/local/nginx/conf/lua # curl 'http://localhost/lua_rewrite_3?jump=1'
Hello Lua!
root@iZ236j3sofdZ:/usr/local/nginx/conf/lua # curl 'http://localhost/lua_rewrite_3?jump=2'
rewrite3 uri : /lua_rewrite_3

即這樣:

rewrite ^ /lua_rewrite_3;             等價於  ngx.req.set_uri("/lua_rewrite_3", false); rewrite ^ /lua_rewrite_3 break;       等價於  ngx.req.set_uri("/lua_rewrite_3", false); 加 if/else判斷/break/return rewrite ^ /lua_rewrite_4 last;        等價於  ngx.req.set_uri("/lua_rewrite_4", true);

注意,在使用rewrite_by_lua時,開啓rewrite_log on;後也看不到相應的rewrite log。

access_by_lua_file

語法:  access_by_lua_file <path-to-lua-script-file>

上下文:http, server, location, location if

做用時期: access tail

做用:用於訪問控制,好比咱們只容許內網ip訪問,可使用以下形式

location /lua_access_1 { default_type "text/html"; access_by_lua_file /usr/local/nginx/conf/lua/lua_access_1.lua; echo "access_ "; }

lua_access_1.lua 添加如下內容:

if ngx.req.get_uri_args()["token"] ~= "123" then return ngx.exit(403) end  

測試輸出:

root@iZ236j3sofdZ:/usr/local/nginx/conf/lua # curl 'http://localhost/lua_access_1?token=123'
access_ 
root@iZ236j3sofdZ
:/usr/local/nginx/conf/lua # curl 'http://localhost/lua_access_1?token=1234' <html> <head><title>403 Forbidden</title></head> <body bgcolor="white"> <center><h1>403 Forbidden</h1></center> <hr><center>nginx/1.7.9</center> </body> </html>

即若是訪問如http://localhost/lua_access?token=234將獲得403 Forbidden的響應。這樣咱們能夠根據如cookie/用戶token來決定是否有訪問權限。

在執行Redis寫入數據的時候,出現一下錯誤:

root@iZ236j3sofdZ:/usr/local/nginx/conf/lua # curl 'http://localhost/lua_redis_basic'
set msg error : ERR wrong number of arguments for 'set' command
location /lua_redis_basic { default_type 'text/html'; lua_code_cache on; //在這裏的緩存是打開的,修改成 lua_code_cache off; 就能夠了 
    content_by_lua_file /usr/local/nginx/conf/lua/test_redis_basic.lua; }

header_filter_by_lua

語法:header_filter_by_lua <lua-script-str>

上下文:http,服務器,位置,位置若是

phase:output-header-filter

警告自從v0.9.17發行版以來,不鼓勵使用此指令; 請改用新的header_filter_by_lua_block指令。

使用<lua-script-str>中指定的Lua代碼定義輸出標頭過濾器。

請注意,此上下文中當前禁用瞭如下API函數:

輸出API函數(例如,ngx.say 和ngx.send_headers)
控制API函數(例如ngx.redirect和ngx.exec)
子請求API函數(例如,ngx.location.capture 和 ngx.location.capture_multi)
Cosocket API函數(例如,ngx.socket.tcp和ngx.req.socket)。
如下是咱們的Lua頭過濾器中覆蓋一個響應頭(或者若是不存在的話)的例子:

location =/header_filter_by_lua {
     proxy_pass http://www.tinywan.com;
     header_filter_by_lua 'ngx.header.Names = "Tinywan"';
}

執行結果:

header_filter_by_lua_block

語法: header_filter_by_lua_block {lua-script}

上下文: http,服務器,位置,位置若是

phase: output-header-filter

相似於header_filter_by_lua指令,除了該指令直接在一對花括號({})中內聯Lua源代碼,而不是以NGINX字符串文字(須要特殊字符轉義)內。

例如:

 header_filter_by_lua_block { ngx.header [「content-length」] = nil }

body_filter_by_lua

語法: body_filter_by_lua <lua-script-str>

上下文:http, server, location, location if

階段: 輸出體過濾器

註釋在發佈以後不鼓勵使用此指令v0.9.17。改用body_filter_by_lua_block指令。

使用<lua-script-str>指定的Lua代碼定義輸出體過濾器。

輸入數據塊經過ngx.arg [1](做爲Lua字符串值)傳遞,表示響應正文數據流結束的「eof」標誌經過ngx.arg [2](做爲Lua布爾值)。

在幕後,「eof」標誌只是Nginx鏈連接緩衝區的last_buf(用於主要請求)或last_in_chain(用於子請求)標誌。(在v0.7.14發佈以前,「eof」標誌在子請求中徹底不起做用。)

能夠經過運行如下Lua語句當即停止輸出數據流

return ngx.ERROR

這將截斷響應體,一般會致使不完整和無效的響應。

Lua代碼能夠經過用Lua字符串或Lua表的字符串覆蓋ngx.arg [1],將本身的輸入數據塊的修改版本傳遞給下游的Nginx輸出體過濾器。例如,要轉換響應正文中的全部小寫字母,咱們能夠寫:

location / { proxy_pass http://mybackend; body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])'; }

當設置nil或空Lua字符串值時ngx.arg[1],根本不會將數據塊傳遞到下游的Nginx輸出過濾器。

一樣,也能夠經過將布爾值設置爲ngx.arg [2] 來指定新的「eof」標誌。例如

location /t { echo hello world; echo hiya globe; body_filter_by_lua '          local chunk = ngx.arg[1] if string.match(chunk, "hello") then ngx.arg[2] = true  -- new eof
             return
         end

         -- just throw away any remaining chunk data
         ngx.arg[1] = nil
     ';
 }

也就是說,當身體過濾器看到包含單詞「hello」的塊時,它將當即將「eof」標誌設置爲true,致使截斷但仍然有效的響應。

當Lua代碼可能改變響應體的長度時,須要老是清除Content-Length標題過濾器中的響應標題(若是有的話)來強制執行流輸出,如

 location /foo { # fastcgi_pass/proxy_pass/... header_filter_by_lua_block { ngx.header.content_length = nil } body_filter_by_lua 'ngx.arg[1] = string.len(ngx.arg[1]) .. "\\n"'; }

請注意,因爲NGINX輸出過濾器當前實現的限制,如下API功能目前在此上下文中被禁用:

能夠爲單個請求調用Nginx輸出過濾器屢次,由於響應主體可能以塊形式傳送。所以,在此指令中指定的Lua代碼也可能在單個HTTP請求的生存期內屢次運行。

該指令在v0.5.0rc32發行版中首次引入。

body_filter_by_lua_block

語法: body_filter_by_lua_block {lua-script-str}

上下文:  http, server, location, location if

階段: 輸出體過濾器

相似於body_filter_by_lua指令,除了該僞指令直接在一對花括號({})中內嵌Lua源代碼,而不是以NGINX字符串文字(須要特殊字符轉義)內。

 body_filter_by_lua_block { local data, eof = ngx.arg[1], ngx.arg[2] }

該指令在v0.9.17發行版中首次引入。

body_filter_by_lua_file

語法: body_filter_by_lua_file <path-to-lua-script-file>

上下文:http, server, location, location if

階段: 輸出體過濾器

至關於body_filter_by_lua,除了指定的文件<path-to-lua-script-file>包含Lua代碼,或者從v0.5.0rc32發行版中,要執行的Lua / LuaJIT字節碼

foo/bar.lua給出類似路徑時,它們將在啓動Nginx服務器時相對於server prefix-p PATH命令行選項肯定的路徑變爲絕對路徑。

該指令在v0.5.0rc32發行版中首次引入。

log_by_lua

語法: log_by_lua <lua-script-str>

上下文:http, server, location, location if

階段: 日誌

註釋在發佈以後不鼓勵使用此指令v0.9.17。請改用log_by_lua_block指令。

<lua-script-str>log請求處理階段內聯Lua源代碼。這不會替代當前的訪問日誌,而是在以前運行。

請注意,此上下文中當前禁用瞭如下API函數

如下是收集$ upstream_response_time的平均數據的示例

lua_shared_dict log_dict 5M; server { location / { proxy_pass http://mybackend; log_by_lua '              local log_dict = ngx.shared.log_dict local upstream_time = tonumber(ngx.var.upstream_response_time) local sum = log_dict:get("upstream_time-sum") or 0 sum = sum + upstream_time log_dict:set("upstream_time-sum", sum) local newval, err = log_dict:incr("upstream_time-nb", 1) if not newval and err == "not found" then log_dict:add("upstream_time-nb", 0) log_dict:incr("upstream_time-nb", 1) end
         ';
 } location = /status { content_by_lua_block { local log_dict = ngx.shared.log_dict local sum = log_dict:get("upstream_time-sum") local nb = log_dict:get("upstream_time-nb") if nb and sum then ngx.say("average upstream response time: ", sum / nb, " (", nb, " reqs)") else ngx.say("no data yet") end } } }

該指令在v0.5.0rc31發行版中首次引入。

 

balancer_by_lua_block

語法: balancer_by_lua_block {lua-script}

上下文: upstream

階段: content

該指令對由upstream {}配置塊定義的任何上游實體運行Lua代碼做爲上游平衡器。

 upstream foo { server 127.0.0.1; balancer_by_lua_block { -- use Lua to do something interesting here
         -- as a dynamic balancer
 } } server { location / { proxy_pass http://foo; } }

生成的Lua負載均衡器能夠與任何現有的nginx上游模塊(如ngx_proxy和 ngx_fastcgi)配合使用

此外,Lua負載均衡器可使用標準上游鏈接池機制,即標準保持活動指令。只需確保keepalive僞指令在單個配置塊中balancer_by_lua_block僞指令以後使用upstream {}

Lua負載平衡器能夠徹底忽略upstream {}塊中定義的服務器列表,並經過lua- resty -core庫中的ngx.balancer模塊從徹底動態的服務器列表中選擇對等體(甚至根據請求進行更改) 。

當nginx上游機制在指令所指定的條件(如proxy_next_upstream 指令)上重試請求時,由此指令註冊的Lua代碼處理程序可能在單個下游請求中被屢次調用。

這個Lua代碼執行上下文不支持屈服,所以在這種狀況下禁用可能產生的Lua API(如cosockets和「light threads」)。一般能夠經過在早期階段處理程序(如access_by_lua *)中執行此類操做 並經過ngx.ctx表將結果傳遞到此上下文中來解決此限制

該指令在v0.10.0發行版中首次引入。

lua_shared_dict

語法: lua_shared_dict <name> <size>

默認值: 

上下文: http

階段: 取決於使用

聲明一個共享內存區域,<name>做爲基於shm的Lua字典的存儲空間ngx.shared.<name>

共享內存區域始終由當前nginx服務器實例中的全部nginx工做進程共享。

<size>參數接受大小的單位,如km

 http {
      lua_shared_dict dogs 10m ;
     ...
 }

硬編碼的最小大小爲8KB,實際最小尺寸取決於實際的用戶數據集(有些人以12KB開頭)。

詳見ngx.shared.DICT

該指令在v0.3.1rc22發行版中首次引入。

ngx.shared.DICT.get

語法: value,flags = ngx.shared.DICT:get(key)

上下文: set_by_lua *,rewrite_by_lua *,access_by_lua *,content_by_lua *,header_filter_by_lua *,body_filter_by_lua *,log_by_lua *,ngx.timer。*,balancer_by_lua *,ssl_certificate_by_lua *,ssl_session_fetch_by_lua *,ssl_session_store_by_lua *

檢索字典中的價值ngx.shared.DICT的關鍵key。若是密鑰不存在或已通過期,那麼nil將被返回。

若是出現錯誤,nil將返回描述錯誤的字符串。

返回的值在插入字典時將具備原始數據類型,例如Lua布爾值,數字或字符串。

該方法的第一個參數必須是字典對象自己,例如,

 local cats = ngx.shared.cats local value, flags = cats.get(cats, "Marry")

或使用Lua的語法糖進行方法調用

 local cats = ngx.shared.cats local value, flags = cats:get("Marry")

這兩種形式基本相同。

若是用戶標誌是0(默認),則不會返回標誌值。

該功能首次在v0.3.1rc22版本中引入。

另請參見ngx.shared.DICT

lua_socket_pool_size

語法:lua_socket_pool_size <size>

默認值:lua_socket_pool_size 30

上下文:http,服務器,位置

指定與每一個遠程服務器相關聯的每一個cosocket鏈接池的大小限制(以鏈接數計)(即由主機端口對或unix域套接字文件路徑標識)。

每一個池默認爲30個鏈接

當鏈接池超過可用的大小限制時,已經在池中的最近最少使用(空閒)鏈接將關閉,覺得當前鏈接騰出空間。

請注意,cosocket鏈接池是每一個nginx工做進程,而不是每一個nginx服務器實例,所以此處指定的大小限制也適用於每一個單個nginx工做進程。

該指令首先在v0.5.0rc1發行版中引入。

lua_socket_keepalive_timeout

語法:lua_socket_keepalive_timeout <time>

默認值:lua_socket_keepalive_timeout 60s

上下文:http,服務器,位置

該指令控制在cosocket內置鏈接池中鏈接的默認最大空閒時間。當此超時達到時,空閒鏈接將被關閉並從池中刪除。這個設置能夠被cosocket對象的setkeepalive方法所覆蓋。

<time>參數能夠是整數,具備可選的時間單位,如s(秒),毫秒(毫秒),m(分鐘)。默認時間單位爲秒,即「秒」。默認設置爲60秒

該指令首先在v0.5.0rc1發行版中引入。

實際使用案例

Redis使用案例

鏈接池:創建TCP鏈接須要三次握手而釋放TCP鏈接須要四次握手,而這些往返時延僅須要一次,之後應該複用TCP鏈接,此時就能夠考慮使用鏈接池,即鏈接池能夠複用鏈接。 咱們只須要將以前的close_redis函數改造爲以下便可:

local function close_redis(red) if not red then  
        return  
    end  
    --釋放鏈接(鏈接池實現) 
    local pool_max_idle_time = 10000 --毫秒 
    local pool_size = 100 --鏈接池大小 
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.say("set keepalive error : ", err) end  
end 

即設置空閒鏈接超時時間防止鏈接一直佔用不釋放;設置鏈接池大小來複用鏈接。

此處假設調用red:set_keepalive(),鏈接池大小經過nginx.conf中http部分的以下指令定義:

#默認鏈接池大小,默認30

lua_socket_pool_size 30;

#默認超時時間,默認60s

lua_socket_keepalive_timeout 60s;

 

注意:

一、鏈接池是每Worker進程的,而不是每Server的;

二、當鏈接超過最大鏈接池大小時,會按照LRU算法回收空閒鏈接爲新鏈接使用;

三、鏈接池中的空閒鏈接出現異常時會自動被移除;

四、鏈接池是經過ip和port標識的,即相同的ip和port會使用同一個鏈接池(即便是不一樣類型的客戶端如Redis、Memcached);

五、鏈接池第一次set_keepalive時鏈接池大小就肯定下了,不會再變動;

五、cosocket的鏈接池http://wiki.nginx.org/HttpLuaModule#tcpsock:setkeepalive

 

Openresty-Lua動態修改upstream後端服務:https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md

相關文章
相關標籤/搜索