Lua-Nginx-Module常用指令(上)
註意:本書使用的Lua-Nginx-Module版本是0.10.13。Nginx API for Lua將被簡稱為Lua API,而Lua-Nginx-Module則被簡稱為Ngx_lua。後面章節中涉及到的Lua API大部分是包含參數的,如果參數以?結尾,代表這個參數是可選的,如在指令ngx.req.get_headers (max_headers?, raw?)中,max_headers和raw是可選的。
一、Nginx和OpenResty
首先,來認識一下OpenResty,它是一個基於Nginx和Lua開發的高性能的Web平臺,包含大量成熟的第三方庫,可快速搭建出高性能的Web服務器,支持常用的反向代理、網關系統、Web應用等。
如果在Nginx上使用Ngx_lua,需要先進行編譯;而OpenResty已經包含此模塊,不需要再進行編譯了。讀者可以自由選擇使用Nginx或OpenResty來搭建服務,如果無法抉擇,可參考如下場景。
- 使用Nginx編譯Ngx_Lua的場景
HTTP代理服務器:復雜度較小,只需部分組件即可,且代理服務器一般由運維人員進行維護。使用Nginx的穩定版進行編譯,在性能方面會更有保障,而OpenResty是Nginx的主線版,可能會不定期更新。
- OpenResty的使用場景
API服務:業務需求多,需要大量組件。
網關系統:需要大量組件和指令來實現動態組件功能。
Web應用服務器:業務服務、頁面服務等,如詳情頁業務的開發。
使用Nginx編寫的Lua代碼都可以直接遷移到OpenResty上;反之卻不一定可行,畢竟OpenResty的組件更多。
二、安裝Ngx_lua
請先安裝LuaJIT 2.1.0-beta3(詳見第6.2節)並需要編譯ngx_devel_kit模塊。
下面是在Nginx上的安裝方式(OpenResty自帶此模塊,不必安裝編譯):
# wget ‘http://nginx.org/download/nginx-1.12.2.tar.gz‘ # git clone https://github.com/simplresty/ngx_devel_kit.git # git clone https://github.com/openresty/lua-nginx-module.git # tar -xzvf nginx-1.12.2.tar.gz # cd nginx-1.12.2/ # ./configure --prefix=/usr/local/nginx_1.12.2 --add-module=../ngx_devel_kit --add-module=../lua-nginx-module --with-ld-opt="-Wl,-rpath,$LUAJIT_LIB" # make && make install
並不是每個Nginx版本都支持最新的Ngx_lua,目前已知支持最新Ngx_lua的Nginx版本如下:
1.13.x (last tested: 1.13.6)
1.12.x
1.11.x (last tested: 1.11.2)
1.10.x
1.9.x (last tested: 1.9.15)
1.8.x
1.7.x (last tested: 1.7.10)
1.6.x
如需獲取最新版本的支持動態,請參考https://github.com/openresty/lua-nginx-module# nginx-compatibility。
三、牢記context標識
Ngx_lua API指令和Nginx的指令一樣,都存在配置環境的約束問題,因此在使用過程中要確保指令的環境符合預期,例如:
ngx.var.VARIABLE
語法:ngx.var.VAR_NAME
context(配置環境):set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,header_ filter_by_lua,body_filter_by_lua,log_by_lua*
context即配置環境,第一次接觸Ngx_lua的讀者看到這樣的配置環境可能會覺得難以理解,因為這還涉及到Ngx_Lua的執行階段(後面會有介紹)。
四、Hello world
首先,還是來一條經典語句“Hello, world”,在Nginx配置中加入一個server:
server {
listen 80;
server_name testnginx.com;
charset koi8-r;
location = /test {
#設置文件使用的默認MIME-type,將會增加一個Content-Type:text/plain的響應頭
default_type ‘text/plain‘;
-- content_by_lua_block執行階段
content_by_lua_block {
ngx.say(‘Hello,world!‘)
}
}
}
訪問這個server,輸出如下:
# curl -I http://testnginx.com/test
Hello,world!
ngx.say將數據作為響應體輸出,返回給客戶端,並在末尾加上一個回車符。
代碼中用到了content_by_lua_block這個指令塊,它的主要作用是在HTTP的內容處理階段生成數據,詳見第8.6節。
五、避免I/O阻塞
當Nginx和Lua進行讀取磁盤操作時會對Nginx的事件循環造成阻塞,所以在請求中應盡量避免操作磁盤,特別是當文件較大時。
如果Lua使用網絡I/O,為了避免出現阻塞的情況,請使用基於Lua API開發的指令,並使用子請求(將在7.13節介紹)來發送網絡I/O和磁盤I/O。如果需要頻繁讀取磁盤,請分離磁盤I/O的任務和網絡I/O的任務,避免它們相互影響。
六、定義模塊搜索路徑
在開發過程中,常常需要編寫自定義的模塊,或者引入第三方的Lua或C模塊,通過下面的配置可以定義相關模塊的路徑以方便快速查找。
6.1 定義Lua模塊的搜索路徑
lua_package_path用來設置默認的Lua模塊的搜索路徑,並配置在http階段。它支持配置相對路徑和絕對路徑,其中相對路徑是在Nginx啟動時由-p PATH 決定的,如果在啟動Nginx時沒有配置-p PATH,就會使用編譯時--prefix的值,此值一般存放在Nginx的$prefix(也可以用${prefix}來表示)變量中。使用lua_package_path設置Lua模塊搜索路徑的示例如下:
http {
-- lua_package_path在配置中只能出現一次,使用下面的任何一個方法都可以
lua_package_path "/usr/local/nginx_1.12.2/conf/lua_modules/?.lua;;";
lua_package_path "conf/lua_modules/?.lua;;";
lua_package_path "${prefix}conf/lua_modules/?.lua;;";
上述配置中的3種配置方式都指向同一個位置:
第1個是絕對路徑;
第2個是相對路徑,Nginx編譯時用 --prefix=/usr/local/nginx_1.12.2;
第3個也是相對路徑,Nginx編譯時用 --prefix=/usr/local/nginx_1.12.2 或-p PATH 指定的位置。
第1個配置方式的缺點在於寫出了具體文件搜索路徑,遷移代碼時會比較麻煩。第2個配置方式的缺點在於無法和-p PATH一起使用,如果-p換了位置就會導致這個配置無效。對於第3個配置方式,如果-p的位置換了,${prefix}的值會跟著變換,使用起來比較靈活。所以建議使用第3種配置方式來配置。
lua_package_path可以支持設置多個搜索路徑,多個搜索路徑之間使用分號分隔就可以了,如下:
lua_package_path "${prefix}conf/lua_modules/?.lua;/opt/lua/?.lua;;";
註意:上述配置中搜索路徑的最後出現了;;兩個半角分號,代表的是LuaJIT安裝時的原始搜索路徑,如果在前面的搜索路徑裏面無法搜索到需要的模塊,就會依次搜索後面的路徑。
6.2 定義C模塊的搜索路徑
lua_package_cpath:用來設置C模塊的搜索路徑,並配置在http階段。使用方式和lua_package_path一樣,如下:
lua_package_cpath "${prefix}conf/c_md/?.so;/opt/c/?.so;;";
七、讀寫Nginx的內置變量
如果需要讀取Nginx的內置變量可以使用ngx.var.VARIABLE。
語法:ngx.var.VAR_NAME
配置環境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,og_by_lua*
含義:讀寫Nginx的變量值。例如HTTP請求頭、Nginx set的變量、URL參數,甚至Nginx通過正則表達式捕獲的$1、$2等值(獲取方式是ngx.var[1]、ngx.var[2],依此類推)。
示例如下:
server {
listen 80;
server_name testnginx.com;
location ~ ^/([a-z]+)/var.html {
set $a ‘‘;
set $b ‘‘;
set $c ‘‘;
set $d ‘‘;
rewrite_by_lua_block {
local ngx = require "ngx"
--將1賦值給變量a
ngx.var.a = ‘1‘
--獲取HTTP請求頭中user_agent的值並賦值給變量b
ngx.var.b = ngx.var.http_user_agent
--獲取參數test的值賦值給變量c
ngx.var.c = ngx.var.arg_test
--獲取location中正則表達式捕獲的$1的值並賦值給變量d
ngx.var.d = ngx.var[1]
}
echo $a;
echo $b;
echo $c;
echo $d;
}
執行結果如下:
# curl -i ‘http://testnginx.com/nginx/var.html?test=12132&a=2&b=c&dd‘
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Thu, 07 Jun 2018 07:22:32 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
1
curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
12132
nginx
如果是未定義的Nginx變量,是無法直接在Lua中進行讀取的。而且有些變量只能讀取,無法進行修改,如$query_string、$arg_PARAMETER和$http_NAME。
八、控制請求頭
在4.1節中講了Nginx中控制請求頭的指令,在Lua API中也有類似的指令。
8.1 添加請求頭
指令:ngx.req.set_header
語法:ngx.req.set_header(header_name, header_value)
配置環境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua
含義:添加或修改當前HTTP的請求頭,如果請求頭已經存在,則會被替換成新的值。通過此方式設置的請求頭會被繼承到子請求中。
示例:設置一個名為Test_Ngx_Ver,值為1.12.2的請求頭:
ngx.req.set_header("Test_Ngx_Ver", "1.12.2")
ngx.req.set_header支持給同一個請求頭設置多個值,用數組的方式添加:
ngx.req.set_header("Test", {"1", "2"})
多個值的輸出結果:
Test: 1
Test: 2
8.2 清除請求頭
指令:ngx.req.clear_header
語法:ngx.req.clear_header(header_name)
配置環境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua
含義:清除當前請求中指定的請求頭。清除後,如果存在未執行的子請求,則子請求會繼承清除後的請求頭。
示例:
ngx.req.clear_header("Test_Ngx_Ver")
還有一種清除請求頭的方式:
ngx.req.set_header("Test_Ngx_Ver", nil)
8.3 獲取請求頭
指令:ngx.req.get_headers
語法:headers = ngx.req.get_headers(max_headers?, raw?)
配置環境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua*
含義:獲取當前請求的全部請求頭,並返回一個Lua的table類型的數據:
示例:
server {
listen 80;
server_name testnginx.com;
location / {
content_by_lua_block {
local ngx = require "ngx";
local h = ngx.req.get_headers()
for k, v in pairs(h) do
ngx.say(‘Header name: ‘,k, ‘ value:‘,v)
end
--因為是table,所以可以使用下面的方式讀取單個響應頭的值
ngx.say(h["host"])
}
}
}
輸出結果如下:
# curl -i ‘http://testnginx.com/test?=12132&a=2&b=c&dd‘
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Fri, 08 Jun 2018 07:46:38 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
Header name:host value: testnginx.com
Header name:accept value: */*
Header name:user-agent value: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
testnginx.com
Lua-Nginx-Module常用指令(上)