--[[
test
--]]
ngx.header.content_type = "text/plain"; --輸出頭部
local user = ngx.var.arg_user -- 定義user變量並獲取url中的參數 http://localhost?user=hello
local sys = ngx.var.server_name -- 獲取nginx中的變量
ngx.say (user); -- 輸出至頁面
ngx.say (sys);
if user== "spam" then
local res=ngx.location.capture("/lua") -- capture
if res.status == 200 then
ngx.print("capture:",res.body)
end
end
tbl = {"alpha", "beta", "gamma"} --定義table 數組
table.insert(tbl, "delta") --增加數組元素
table.sort( tbl, sortLevelNameAsc ) -- 數組排序
for i = 1, table.getn(tbl) do -- for循環
ngx.say(string.format("%s", tbl[i])) -- string format
end
local deled=table.remove(tbl, 2) -- 刪除指定位置元素並返回刪除元素.如果沒有指定刪除的位置,則默認刪除table的最后一個元素
ngx.say ("delete the second element " .. deled ) -- 字符串連接 ..
gTable = {}
gTable.name = "eric"
gTable.gender = "man"
gTable.phonenumber = "0000000000"
gTable[1] = "公司"
gTable[2] = "部門"
gTable.hobby = {"跑步", "讀書", "游戲", "動漫"} -- 多維table,可以通過gTable.hobby[1]的方式訪問.即gTable.hobby本身也是一個table
gTable.secTable = {}
gTable.secTable.job = "程序員"
gTable.secTable.label = "寫代碼的"
gTable.secTable.description = "職責是實現產品的邏輯"
for index, value in pairs(gTable) do
ngx.say(string.format("%s:%s", index, value)) -- string format
if ("table" == type(value)) then
for idx, var in pairs(value) do
ngx.say(string.format("二維table:%s:%s", idx, var)) -- string format
end
end
end
--[[
輸出結果如下:
1 公司
2 部門
hobby table: 0x7fdceac14bc0
二維table: 1 跑步
二維table: 2 讀書
二維table: 3 游戲
二維table: 4 動漫
phonenumber 0000000000
gender man
secTable table: 0x7fdceac15100
二維table: label 寫代碼的
二維table: description 職責是實現產品的邏輯
二維table: job 程序員
name eric
--]]
ngx.say (table.concat( tbl, ":")) -- join
ngx.say(#tbl) -- 返回數組個數
ngx.say(os.time()) -- 1423643892
ngx.say(os.date()) -- Wed Feb 11 16:38:12 2015
ngx.say(os.date("%m/%d/%Y",os.time())) -- 02/11/2015
ngx.say(os.date("%Y-%m-%d %H:%M:%S",1423643892)) -- 2015-02-11 16:38:12 time to date
ngx.say(os.time{year="2015", month="2", day="11", hour="16",min="38",sec="12"}) -- 1423643892 date to time
ngx.say(tonumber("100")+11) --字符與數字的顯式轉換 輸出結果為:111
ngx.say(type(tostring(100))) --轉換成字符串 並獲取類型 輸出結果為:string
ngx.say(string.len("hello")) --字符串長度 輸出結果為:5
ngx.say(string.sub("hello Lua", 7,9)) --截取字符串 輸出結果為:Lua
ngx.say(math.random(1,2)); --隨機數
ngx.print(ngx.req.raw_header(),ngx.var.uri) -- 獲取http header 信息
--[[
獲取ip
--]]
local function getip( )
local myIP = ngx.req.get_headers()["X-Real-IP"]
if myIP == nil then
myIP = ngx.req.get_headers()["x_forwarded_for"]
end
if myIP == nil then
myIP = ngx.var.remote_addr
end
return myIP
end
ngx.print(getip()) --調取函數 按順序,必須先申明方法 才能調用
ngx.redirect("http://www.elong.com") --跳轉
多行字符串
local longstring=[[123 456 abc 'szSuffix' ]] ngx.say(longstring)
ngx_lua安裝
ngx_lua安裝可以通過下載模塊源碼,編譯Nginx,但是推薦采用openresty。Openresty就是一個打包程序,包含大量的第三方Nginx模塊,比如HttpLuaModule,HttpRedis2Module,HttpEchoModule等。省去下載模塊,並且安裝非常方便。
ngx_openresty bundle: openresty
./configure --with-luajit&& make && make install
默認Openresty中ngx_lua模塊采用的是標准的Lua5.1解釋器,通過--with-luajit使用LuaJIT。
ngx.location.capture
location = /other { ehco 'Hello, world!'; } # Lua非阻塞IO location = /lua { content_by_lua ' local res = ngx.location.capture("/other?id=12") if res.status == 200 then ngx.print(res.body) end '; }
capture post
-- POST https://www.example.com:443/foo/bar?hello=world
ngx.req.set_header("Content-Type", "application/json;charset=utf8"); ngx.req.set_header("Accept", "application/json"); local res = ngx.location.capture('/proxy/https/www.example.com/443/foo/bar', { method = ngx.HTTP_POST, body = body, args = {hello = 'world'} });
ngx.location.capture_multi
語法:res1,res2, ... = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, ...})
與ngx.location.capture功能一樣,可以並行的、非阻塞的發出多個子請求。這個方法在所有子請求處理完成后返回,並且整個方法的運行時間取決於運行時間最長的子請求,並不是所有子請求的運行時間之和。
# 同時發送多個子請求(subrequest) location = /moon { ehco 'moon'; } location = /earth { ehco 'earth'; } location = /lua { content_by_lua ' local res1,res2 = ngx.location.capture_multi({ {"/moon"}, {"earth"} }) if res1.status == 200 then ngx.print(res1.body) end ngx.print(",") if res2.status == 200 then ngx.print(res2.body) end '; }
set_by_lua
和set指令一樣用於設置Nginx變量並且在rewrite階段執行,只不過這個變量是由lua腳本計算並返回的。
語法:set_by_lua$res <lua-script-str> [$arg1 $arg2 ...]
location = /adder { set_by_lua $res " local a = tonumber(ngx.arg[1]) local b = tonumber(ngx.arg[2]) return a + b" $arg_a $arg_b; echo $res; }
set_by_lua_file
執行Nginx外部的lua腳本,可以避免在配置文件中使用大量的轉義
location = /fib { set_by_lua_file $res "conf/adder.lua" $arg_n; echo $res; }
adder.lua:
local a = tonumber(ngx.arg[1])
local b = tonumber(ngx.arg[2])
return a + b
access_by_lua和access_by_lua_file
運行在access階段,用於訪問控制。Nginx原生的allow和deny是基於ip的,通過access_by_lua能完成復雜的訪問控制,比如,訪問數據庫進行用戶名、密碼驗證等。
location /auth { access_by_lua ' if ngx.var.arg_user == "ntes" then return else Ngx.exit(ngx.HTTP_FORBIDDEN) end '; echo 'welcome ntes'; }
輸出:
$ curl 'localhost/auth?user=sohu' $ Welcome ntes $ curl 'localhost/auth?user=ntes' $ <html> <head><title>403 Forbidden</title></heda> <body bgcolor="white"> <center><h1>403 Forbidden</h1></center> <hr><center>ngx_openresty/1.0.10.48</center> </body> </html>
rewrite_by_lua和rewrite_by_lua_file
實現url重寫,在rewrite階段執行。
配置:
location = /foo { rewrite_by_lua 'ngx.exec("/bar")'; echo 'in foo'; } location = /bar { echo 'Hello, Lua!'; }
輸出:
$ curl 'localhost/lua' $ Hello, Lua!
content_by_lua和content_by_lua_file
Contenthandler在content階段執行,生成http響應。由於content階段只能有一個handler,所以在與echo模塊使用時,不能同時生效,我測試的結果是content_by_lua會覆蓋echo。這和之前的hello world的例子是類似的。
location = /lua { content_by_lua 'ngx.say("Hello, Lua!")'; }
os.execute ([command])
功能:相當於C的system函數,返回系統狀態碼
例如:
os.execute("pause")
輸出:
按任意鍵繼續. . .
os.exit ([code])
功能:相當於C的exit函數,終止主程序,code為返回值
例如:
os.exit(1)
os.getenv (varname)
例如:
print(os.getenv("USERDOMAIN")) print(os.getenv("SystemRoot")) print(os.getenv("Os2LibPath")) print(os.getenv("ProgramFiles" )) print(os.getenv("APPDATA" )) print(os.getenv("ALLUSERSPROFILE" )) print(os.getenv("CommonProgramFiles" )) print(os.getenv("COMPUTERNAME" )) print(os.getenv("USERNAME")) print(os.getenv("USERPROFILE" )) print(os.getenv("ComSpec")) print(os.getenv("LOGONSERVER" )) print(os.getenv("NUMBER_OF_PROCESSORS" )) print(os.getenv("OS")) print(os.getenv("PATHEXT" )) print(os.getenv("PROCESSOR_ARCHITECTURE" )) print(os.getenv("PROCESSOR_IDENTIFIER" )) print(os.getenv("PROCESSOR_LEVEL" )) print(os.getenv("PROCESSOR_REVISION" )) print(os.getenv("USERDOMAIN")) print(os.getenv("SystemRoot" )) print(os.getenv("TEMP"))
輸出:
RDEV C:\WINDOWS nil C:\Program Files C:\Documents and Settings\baiyun\Application Data C:\Documents and Settings\All Users C:\Program Files\Common Files BAIYUN baiyun C:\Documents and Settings\baiyun C:\WINDOWS\system32\cmd.exe http://www.cnblogs.com/whiteyun/admin/file://rdev1/ 2 Windows_NT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.py;.pyw;.wlua x86 x86 Family 15 Model 6 Stepping 5, GenuineIntel 15 0605 RDEV C:\WINDOWS C:\DOCUME~1\baiyun\LOCALS~1\Temp
os.remove (filename)
功能:刪除文件或一個空目錄,若函數調用失敗則返加nil加錯誤信息
os.rename (oldname, newname)
功能:更改一個文件或目錄名,若函數調用失敗則返加nil加錯誤信息
調取 mysql json redis操作
ngx.say("=============== mysql ==============") local mysql = require "resty.mysql" local db,err = mysql:new() if not db then ngx.say("failed to instantiate mysql: ",err) return end db:set_timeout(1000) local ok,err,errno,sqlstate = db:connect{ host = "127.0.0.1", port = 3306, database = "el_agency", user = "root", password = "", max_package_size = 1024 } if not ok then ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate) return end
db:query("set names utf8") ngx.say("connected to mysql.") res,err,errno,sqlstate = db:query("select username,appKey,secretKey from tbl_user limit 2") if not res then ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".") return end
db:close()
ngx.say("=============== cjson ==============") local cjson = require "cjson"; local res1=cjson.encode(res); ngx.say(res1); local res2=cjson.decode(res1); print_lua_table(res2); ngx.say("=============== redis ==============") local redis = require("resty.redis") local client = redis:new() client:set_timeout(1000) -- 1 second local ok,err = client:connect("127.0.0.1",6379) if not ok then ngx.say("failed to connect: ",err) return end client:set("hello","world"); local res,err = client:get("hello") if not res then ngx.say("failed to get",err) return end
client:close()
ngx.say(res)
執行順序
1.init_by_lua 上下文http ngx啟動時執行 2.set_by_lua 上下文 server, server if, location, location if 3.rewrite_by_lua 上下文 http, server, location, location if 4.access_by_lua 上下文 http, server, location, location if 5.content_by_lua 上下文 location, location if 6.header_filter_by_lua 上下文 http, server, location, location if 7.body_filter_by_lua 上下文 http, server, location, location if 8.log_by_lua 上下文 http, server, location, location if
lua中 三目運算符的實現
(a and b) or c <==> a ? b : c
lua urlencode urldecode URL編碼
function decodeURI(s) s = string.gsub(s, '%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end) return s end function encodeURI(s) s = string.gsub(s, "([^%w%.%- ])", function(c) return string.format("%%%02X", string.byte(c)) end) return string.gsub(s, " ", "+") end
或者
ngx.escape_uri() 字符串的url編碼
ngx.unescape_uri() 字符串url解碼
LUA require 搜索路徑指定方法
如果是一個 *.LUA 的文件, 里面用到了自己寫的庫, 或者第三方寫的庫, 但是你不想把它放到 lua 的安裝目錄里, 則在代碼里面可以指定require搜索的路徑。 package.path = '/usr/local/share/lua/5.1/?.lua;/home/resty/?.lua;;' --搜索lua模塊 ;;指定默認路徑 package.cpath = '/usr/local/lib/lua/5.1/?.so;;' --搜索so模塊 如果是要在 nginx.conf 文件中引用第三方的庫,則需要在 http 段中添加下面的代碼 lua_package_path '/usr/local/share/lua/5.1/?.lua;/home/resty/?.lua;'; lua_package_cpath '/usr/local/lib/lua/5.1/?.so;';
lua下面dump出一個table的結構
--- @brief 調試時打印變量的值 --- @param data 要打印的字符串 --- @param [max_level] table要展開打印的計數,默認nil表示全部展開 --- @param [prefix] 用於在遞歸時傳遞縮進,該參數不供用戶使用於 --- @ref http://dearymz.blog.163.com/blog/static/205657420089251655186/ function var_dump(data, max_level, prefix) if type(prefix) ~= "string" then prefix = "" end if type(data) ~= "table" then print(prefix .. tostring(data)) else print(data) if max_level ~= 0 then local prefix_next = prefix .. " " print(prefix .. "{") for k,v in pairs(data) do io.stdout:write(prefix_next .. k .. " = ") if type(v) ~= "table" or (type(max_level) == "number" and max_level <= 1) then print(v) else if max_level == nil then var_dump(v, nil, prefix_next) else var_dump(v, max_level - 1, prefix_next) end end end print(prefix .. "}") end end end
ngx.exec
location @cc { internal; root html; index index.html index.htm; } 代碼中 ngx.exec("@cc")
iptolong
-- 參數:待分割的字符串,分割字符 -- 返回:子串表.(含有空串) function split(s, delim) if type(delim) ~= "string" or string.len(delim) <= 0 then return end local start = 1 local t = {} while true do local pos = string.find (s, delim, start, true) -- plain find if not pos then break end table.insert (t, string.sub (s, start, pos - 1)) start = pos + string.len (delim) end table.insert (t, string.sub (s, start)) return t end --ip轉整數 function ip2long(ip) local ips=split(ip,".") local num = 0 for i,v in pairs(ips) do num =num +(tonumber(v) * math.pow(256,#ips-i)) end return num end
獲取當前路徑及上級目錄
function dirname(str) if str:match(".-/.-") then local name = string.gsub(str, "(.*/)(.+)", "%1") return name elseif str:match(".-\\.-") then local name = string.gsub(str, "(.*\\)(.+)", "%1") return name else return '' end end local __FILE__ = debug.getinfo(1,'S').source:sub(2) ROOT_PATH=dirname(__FILE__)
lua 創建一個“類” 並返回“類”中的所有方法
--helper.lua local _M = { _VERSION = '0.09' } function _M.to_hex(s) end function _M.atoi (s) end return _M local helper=require 'helper' var_dump(helper) 返回 ["to_hex"] = function: 0x0cd26038, ["atoi"] = function: 0x0cd260e8, ["_VERSION"] = "0.09",
package.seeall
--game.lua module(..., package.seeall) --- ...不定參數 function play() return "ok just do it"end function quit() return "welcome back again"end
--require local game=require 'game' ngx.say(game:play())
Nginx中Lua模塊的運行機制
Nginx中的每個Worker進程使用一個lua虛擬機,工作進程中的所有協程共享虛擬機。將Nginx中的lua API封裝好之后注入到lua的VM中就可以在lua代碼中進行訪問
異常捕獲pcall
local user = ngx.var.arg_user local st,err=pcall(function()
if user == "spam" then local res=ngx.location.capture("/lua") -- capture if res.status == 200 then ngx.print("capture:",res.body) end else error("出錯啦") end
end); if not st then ngx.say(err) end
----類似於php
try{
if (user == "spam"){
//todo }else{ throw New Exception("出錯啦") }
}catch(Exception $ex){
echo $ex->getMessage();
}