(轉)Ngx_Lua使用分享

原文:http://www.javashuo.com/article/p-tmbettdi-dn.htmlphp

ngx_lua 模塊詳細講解(基於openresty)---http://www.javashuo.com/article/p-vbroczys-dv.htmlhtml

用lua擴展你的Nginx(整理)-----------------http://www.javashuo.com/article/p-uwhaxwxl-ge.htmlpython

ngx_lua模塊中的共享內存字典項API-----https://blog.csdn.net/weiyuefei/article/details/38487475mysql

1. Nginx_Lua

1.1. 介紹

ngx_lua – 把lua語言嵌入nginx中,使其支持lua來快速開發基於nginx下的業務邏輯nginx

該模塊不在nginx源碼包中,需自行下載編譯安裝。使用lua 5.1(目前不支持lua 5.2) 或 luajit 2.0 。git

添加lua支持後,開發複雜的模塊,週期快,依然是100%異步非阻塞。github

ngx_lua 哪些人在用:sql

淘寶、騰訊財經、網易財經、360、去哪兒網等api

CloudFlare, CNN, Wingify, Reblaze, Turner, Broadcasting System緩存

該項目主要開發者:

chaoslawful Taobao, Alibaba Grp.

agentzh CloudFlare

https://github.com/chaoslawful/lua-nginx-module

1.2. 安裝

1.2.1. 安裝JIT平臺

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

1.2.2. NDK與Lua_module

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

1.2.3. 編譯安裝Nginx

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

1.3. 嵌入Lua後

1.3.1. 檢測版本

本身編譯官方的 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]#

1.3.2. Hello,World

在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

1.3.3. 同步形式,異步執行

咱們假定,同時要訪問多個數據源,並且,查詢是沒有依賴關係,那咱們就能夠同時發出請求

這樣我總的延時, 是我全部請求中最慢的一個所用時間,而不是原先的全部請求用時的疊加

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。

1.4. Nginx與Lua執行順序

1.4.1. Nginx順序

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

1.4.2. Lua順序

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

2. Lua基本語法

2.1. 關鍵字

and	break	do	else	elseif	
end	false	for	function	if	
in	local	nil	not	or	
repeat	return	then	true	until	while

2.2. 運算

2.2.1. 數字運算

支持 +, -, *, /,^  好比2^3 結果爲8, 2^4結果爲16。
鏈接兩個字符串,用".."運處符, php爲".", JAVA、C#爲"+"

2.2.2. 賦值運算

a,b,c,d=1,2,3,4   -- 多變量一塊兒賦值
a,b=b,a  --交換變量功能
在默認狀況下,變量老是認爲是全局的。假如須要定義局部變量,則在第一次賦值的時候,須要用local說明。好比:
local a,b,c = 1,2,3  -- a,b,c都是局部變量

2.2.3. 邏輯運算

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 。

2.3. 條件判斷語句

2.3.1. if

if 條件 then 
      ... 
elseif 條件 then
      ... else ... 
end

2.3.2. repeat

repeat ... until 條件

2.3.3. while

while 條件 
do 
      ... 
end

2.3.4. for

for 變量=初值, 終點值, 步進 
do
      ... 
end
for 變量1, 變量2, ... 變量n in 表或枚舉函數 
do 
       ... 
end

2.4. 經常使用結構

2.4.1. table

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自動編號開始,能夠獲取

2.4.2. lua表空判斷

在項目的腳本lua中常常有這樣的需求

local a = {}
判斷表a是否爲空,有提供api table.maxn(a) 或 #a 獲取表a長度,可是該狀況只
針對key爲數字且安裝遞增方式。

對於自定義key的狀況下,獲取表長度將會失敗,可經過lua內置 next函數解決該問題。

2.5. 參考

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
相關文章
相關標籤/搜索