ngx_lua – 把lua語言嵌入nginx中,使其支持lua來快速開發基於nginx下的業務邏輯php
該模塊不在nginx源碼包中,需自行下載編譯安裝。使用lua 5.1(目前不支持lua 5.2) 或 luajit 2.0 。html
添加lua支持後,開發複雜的模塊,週期快,依然是100%異步非阻塞。python
ngx_lua 哪些人在用:mysql
淘寶、騰訊財經、網易財經、360、去哪兒網等nginx
CloudFlare, CNN, Wingify, Reblaze, Turner, Broadcasting Systemgit
該項目主要開發者:github
chaoslawful Taobao, Alibaba Grp.sql
agentzh CloudFlareapi
https://github.com/chaoslawful/lua-nginx-module緩存
JIT
一般,程序有兩種運行方式:靜態編譯與動態直譯。
靜態編譯的程序在執行前所有被翻譯爲機器碼,而動態直譯執行的則是一句一句邊運行邊翻譯。
即時編譯(Just-In-Time Compiler)則混合了這兩者,一句一句編譯源代碼,可是會將翻譯過的代碼緩存起來以下降性能損耗。
JAVA、.NET 實現都使用即時編譯以提供高速的代碼執行。
注意:
在nginx.conf中添加"lua_code_cache on/off",來開啓是否將代碼緩存,因此每次變動"*.lua"文件時,必須reload nginx纔可生效。
僅針對"set_by_lua_file, content_by_lua_file, rewrite_by_lua_file, and access_by_lua_file"有效, 由於其餘爲寫在配置文件
中,更改代碼也必須reload nginx。在生產環境下,不能禁用cache。同時在lua代碼中使用"dofile" 或 "loadfie" 來加載外部lua腳步
將不會對它進行緩存,應該使用"require"來代替。禁用cache,當且僅當在調式代碼下。
demo 1
LuaJIT
是一個利用JIT編譯技術把Lua腳本直接編譯成機器碼由CPU運行
版本:2.0 與 2.1
當前穩定版本爲 2.0。
2.1爲版本與ngx_lua將有較大性能提高,主要是CloudFlare公司對luajit的捐贈。
FFI庫,是LuaJIT中最重要的一個擴展庫。
1. 它容許從純Lua代碼調用外部C函數,使用C數據結構;
2. 就不用再像Lua標準math庫同樣,編寫Lua擴展庫;
3. 把開發者從開發Lua擴展C庫(語言/功能綁定庫)的繁重工做中釋放出來;
demo 2
下載編譯
wget -c http://luajit.org/download/LuaJIT-2.0.2.tar.gz
tar xzvf LuaJIT-2.0.2.tar.gz
cd LuaJIT-2.0.2
make install PREFIX=/usr/local/luajit
echo "/usr/local/luajit/lib" > /etc/ld.so.conf.d/usr_local_luajit_lib.conf
ldconfig
#注意環境變量!
export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0
NDK(Nginx Development Kit)模塊是一個拓展Nginx服務器核心功能的模塊
第三方模塊開發能夠基於它來快速實現
NDK提供函數和宏處理一些基本任務,減輕第三方模塊開發的代碼量。
wget -c https://github.com/simpl/ngx_devel_kit/archive/v0.2.18.tar.gz
wget -c https://github.com/chaoslawful/lua-nginx-module/archive/v0.8.6.tar.gz
tar xzvf v0.2.18
tar xzvf v0.8.6
wget -c http://nginx.org/download/nginx-1.4.2.tar.gz
tar xzvf nginx-1.4.2.tar.gz
cd nginx-1.4.2
./configure --add-module=../ngx_devel_kit-0.2.18/ --add-module=../lua-nginx-module-0.8.6/
make
make install
本身編譯官方的 nginx 源碼包,只需事前指定 LUAJIT_INC 和 LUAJIT_LIB 這兩個環境變量。
驗證你的 LuaJIT 是否生效,能夠經過下面這個接口:
location = /lua-version {
content_by_lua '
if jit then
ngx.say(jit.version)
else
ngx.say(_VERSION)
end
';
}
demo 3
若是使用的是標準 Lua,訪問 /lua-version 應當返回響應體 Lua 5.1
若是是 LuaJIT 則應當返回相似 LuaJIT 2.0.2 這樣的輸出。
不要使用標準lua,應當使用luajit, 後者的效率比前者高多了。
也能夠直接用 ldd 命令驗證是否鏈了 libluajit-5.1 這樣的 .so 文件,例如:
[root@limq5 sbin]# ldd nginx | grep lua
libluajit-5.1.so.2 => /usr/local/luajit/lib/libluajit-5.1.so.2 (0x00007f48e408b000)
[root@limq5 sbin]#
在nginx.conf中的service添加一個location。
location = /test {
content_by_lua '
ngx.say("Hello World")
ngx.log(ngx.ERR, "err err err")
';
}
demo 4
用戶訪問 http://localhost/test 將會打印出「Hello World」內容。
ngx.say 是 lua 顯露給模塊的接口。
相似的有 ngx.log(ngx.DEBUG, 「」),能夠在error.log輸出調試信息。
另外也能夠調用外部腳本,如同咱們寫php、jsp應用時,業務腳本單獨組織在.php或.jsp文件中同樣
location = /test2 {
content_by_lua_file conf/lua/hello.lua;
}
文件hello.lua內容以下:
ngx.say("Hello World")
這裏的腳本能夠任意複雜,也可使用Lua 本身的庫
lua可用模塊列表:
http://luarocks.org/repositories/rocks/
安裝相似yum,它也有一個倉庫:
luarocks install luafilesystem
運行上面命令後,會編譯一個 「lfs.so」, 文件,拷貝文件到nginx定義的LUA_PATH中,而後引用
該庫,就可調用其中函數。
LUA_PATH:
lua_package_path ‘/opt/17173/nginx-ds/conf/lua/?.lua;;’
lua_package_cpath ‘/opt/17173/nginx-ds/conf/lua/lib/?.so;/usr/local/lib/?.?;;’;
其中」;;」表明原先查找範圍。
demo 5
咱們假定,同時要訪問多個數據源,並且,查詢是沒有依賴關係,那咱們就能夠同時發出請求
這樣我總的延時, 是我全部請求中最慢的一個所用時間,而不是原先的全部請求用時的疊加
location = /api {
content_by_lua '
local res1, res2, res3 =
ngx.location.capture_multi{
{"/memc"}, {"/mysql"}, {"/postgres"}
}
ngx.say(res1.body, res2.body, res3.body)
';
}
demo 6
ngx.location.capture 沒法跨server進行處理, 只能在同一個server下的不一樣location。
Nginx 處理每個用戶請求時,都是按照若干個不一樣階段(phase)依次處理的,而不是根據配置文件上的順序。
Nginx 處理請求的過程一共劃分爲 11 個階段,按照執行順序依次是
post-read、server-rewrite、find-config、rewrite、post-rewrite、 preaccess、access、post-access、try-files、content、log.
post-read:
讀取請求內容階段
Nginx讀取並解析完請求頭以後就當即開始運行
例如模塊 ngx_realip 就在 post-read 階段註冊了處理程序,它的功能是迫使 Nginx 認爲當前請求的來源地址是指定的某一個請求頭的值。
server-rewrite
Server請求地址重寫階段
當 ngx_rewrite 模塊的set配置指令直接書寫在 server 配置塊中時,基本上都是運行在 server-rewrite 階段
find-config
配置查找階段
這個階段並不支持 Nginx 模塊註冊處理程序,而是由 Nginx 核心來完成當前請求與 location 配置塊之間的配對工做。
rewrite
Location請求地址重寫階段
當 ngx_rewrite 模塊的指令用於 location 塊中時,即是運行在這個 rewrite 階段。
另外,ngx_set_misc(設置md五、encode_base64等) 模塊的指令,還有 ngx_lua 模塊的 set_by_lua 指令和 rewrite_by_lua 指令也在此階段。
post-rewrite
請求地址重寫提交階段
由 Nginx 核心完成 rewrite 階段所要求的「內部跳轉」操做,若是 rewrite 階段有此要求的話。
preaccess
訪問權限檢查準備階段
標準模塊 ngx_limit_req 和 ngx_limit_zone 就運行在此階段,前者能夠控制請求的訪問頻度,然後者能夠限制訪問的併發度。
access
訪問權限檢查階段
標準模塊 ngx_access、第三方模塊 ngx_auth_request 以及第三方模塊 ngx_lua 的 access_by_lua 指令就運行在這個階段。
配置指令可能是執行訪問控制性質的任務,好比檢查用戶的訪問權限,檢查用戶的來源 IP 地址是否合法
post-access
訪問權限檢查提交階段
主要用於配合 access 階段實現標準 ngx_http_core 模塊提供的配置指令 satisfy 的功能。
satisfy all(與關係)
satisfy any(或關係)
try-files
配置項try_files處理階段
專門用於實現標準配置指令 try_files 的功能
若是前 N-1 個參數所對應的文件系統對象都不存在,try-files 階段就會當即發起「內部跳轉」到最後一個參數(即第 N 個參數)所指定的 URI.
content
內容產生階段
Nginx 的 content 階段是全部請求處理階段中最爲重要的一個,由於運行在這個階段的配置指令通常都肩負着生成「內容」
並輸出 HTTP 響應的使命。
log
日誌模塊處理階段
記錄日誌
淘寶有開放一個nginx開發手冊,裏面包含不少有用的資料
http://tengine.taobao.org/book/
做者的google論壇:
https://groups.google.com/forum/#!forum/openresty
Nginx下Lua處理階段與使用範圍:
init_by_lua http
set_by_lua server, server if, location, location if
rewrite_by_lua http, server, location, location if
access_by_lua http, server, location, location if
content_by_lua location, location if
header_filter_by_lua http, server, location, location if
body_filter_by_lua http, server, location, location if
log_by_lua http, server, location, location if
timer
init_by_lua:
在nginx從新加載配置文件時,運行裏面lua腳本,經常使用於全局變量的申請。
例如lua_shared_dict共享內存的申請,只有當nginx重起後,共享內存數據才清空,這經常使用於統計。
set_by_lua:
設置一個變量,經常使用與計算一個邏輯,而後返回結果
該階段不能運行Output API、Control API、Subrequest API、Cosocket API
rewrite_by_lua:
在access階段前運行,主要用於rewrite
access_by_lua:
主要用於訪問控制,能收集到大部分變量,相似status須要在log階段纔有。
這條指令運行於nginx access階段的末尾,所以老是在 allow 和 deny 這樣的指令以後運行,雖然它們同屬 access 階段。
content_by_lua:
階段是全部請求處理階段中最爲重要的一個,運行在這個階段的配置指令通常都肩負着生成內容(content)並輸出HTTP響應。
header_filter_by_lua:
通常只用於設置Cookie和Headers等
該階段不能運行Output API、Control API、Subrequest API、Cosocket API
body_filter_by_lua:
通常會在一次請求中被調用屢次, 由於這是實現基於 HTTP 1.1 chunked 編碼的所謂「流式輸出」的。
該階段不能運行Output API、Control API、Subrequest API、Cosocket API
log_by_lua:
該階段老是運行在請求結束的時候,用於請求的後續操做,如在共享內存中進行統計數據,若是要高精確的數據統計,應該使用body_filter_by_lua。
該階段不能運行Output API、Control API、Subrequest API、Cosocket API
timer:
可參考官方文檔:
http://wiki.nginx.org/HttpLuaModule
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
支持 +, -, *, /,^ 好比2^3 結果爲8, 2^4結果爲16。
鏈接兩個字符串,用".."運處符, php爲".", JAVA、C#爲"+"
a,b,c,d=1,2,3,4 -- 多變量一塊兒賦值
a,b=b,a --交換變量功能
在默認狀況下,變量老是認爲是全局的。假如須要定義局部變量,則在第一次賦值的時候,須要用local說明。好比:
local a,b,c = 1,2,3 -- a,b,c都是局部變量
and, or, not
在Lua中,只有false和nil才計算爲false,其它任何數據都計算爲true,0也是true
and 和 or的運算結果不是true和false,而是和它的兩個操做數相關。
a and b:若是a爲false,則返回a;a true 返回b
a or b:若是 a 爲true,則返回a;a false 返回b
模擬C語言中的語句:x = a? b : c,在Lua中,能夠寫成:x = a and b or c。
最有用的語句是: x = x or v,它至關於:if not x then x = v end 。
if 條件 then
...
elseif 條件 then
... else ...
end
repeat ... until 條件
while 條件
do
...
end
for 變量=初值, 終點值, 步進
do
...
end
for 變量1, 變量2, ... 變量n in 表或枚舉函數
do
...
end
1. table 是 lua 中最重要的數據類型。
2. table 相似於 python 中的字典。
3. table 只能經過構造式來建立
;例1
Lua代碼
tab = { a = 10, b = 20, c = 30, d = 40 }
print(tab["a"])
註釋:
1)table 中的每項要求是 key = value 的形式
2)key 只能是字符串, 這裏的 a, b, c, d 都是字符串,可是不能加上引號
3)經過 key 來訪問 table 的值,這時候, a 必須加上引號
;例2
tab = { 10, m = 20, 11, 12, 13 }
print(tab[1]) = 10
print(tab[2]) = 11
print(tab[3]) = 12
print(tab[4]) = 13
獲取表長度:
print(table.getn(tab)) -> 4
print(#tab) -> 4
遍歷表:
for k, v in pairs(tab) do
print(k, ":", v)
end
1 : 10
2 : 11
3 : 12
4 : 13
m : 20
註釋:
1)省略key,會自動以1開始編號,並跳過設置過的key
2)獲取表長度,只有當表使用1自動編號開始,能夠獲取
在項目的腳本lua中常常有這樣的需求
local a = {}
判斷表a是否爲空,有提供api table.maxn(a) 或 #a 獲取表a長度,可是該狀況只
針對key爲數字且安裝遞增方式。
對於自定義key的狀況下,獲取表長度將會失敗,可經過lua內置 next函數解決該問題。
lua官方手冊 http://www.lua.org/manual/5.1/
lua中文翻譯手冊 http://www.codingnow.com/2000/download/lua_manual.html
轉載:http://17173ops.com/2013/11/01/17173-ngx-lua-manual.shtml