文章目錄
一、基礎
1、常用
OpenResty 中文官網:https://openresty.org/cn/
包管理:https://opm.openresty.org/
(1)Lua 的下標從 1 開始
(2)使用 … 來拼接字符串
2、使用局部變量
在 Lua 中,變量默認是全局的,會被放到名為 _G 的 table 中
。不加 local 的變量會在全局表中查找,這是昂貴的操作。如果再加上一些變量名的拼寫錯誤,就會造成難以定位的 bug。
建議總是使用 local 來聲明變量,即使在 require module 的時候也是一樣:
-- Recommended
local xxx = require('xxx')-- Avoid
require('xxx')local ngx_re = require "ngx.re"
local ngx = require "ngx"
--No
local function foo()local ok, err = ngx.timer.at(delay, handler)
end--Yes
local timer_at = ngx.timer.atlocal function foo()local ok, err = timer_at(delay, handler)
end
-- 為了風格的統一,require 和 ngx 也需要 local 化:
--No
local core = require("apisix.core")
local timer_at = ngx.timer.atlocal function foo()local ok, err = timer_at(delay, handler)
end--Yes
local ngx = ngx
local require = require
local core = require("apisix.core")
local timer_at = ngx.timer.atlocal function foo()local ok, err = timer_at(delay, handler)
end
3、模塊化
-- hello.lua定義一個對象,并返回
local _M = {}_M.color = {red = 1,blue = 2,green = 3}return _M
location / {content_by_lua_block {-- 引入hello,并用一個變量接收,這個變量就可以調用對象的方法和變量了local hello = require "hello"ngx.say(hello.color.green)}
}
二、性能提升
1、使用fft調用shell
-- 不要用阻塞的lua語法
os.execute("kill -HUP " .. pid)
os.execute(" cp test.exe /tmp ")
os.execute(" openssl genrsa -des3 -out private.pem 2048 ")-- 使用lua-resty-signal 這個 OpenResty 自帶的庫
local resty_signal = require "resty.signal"
local pid = 12345
local ok, err = resty_signal.kill(pid, "KILL")-- 使用基于 ngx.pipe 的 lua-resty-shell 庫
$ resty -e 'local shell = require "resty.shell"
local ok, stdout, stderr, reason, status =shell.run([[echo "hello, world"]])ngx.say(stdout) '
2、不要在循環中拼接字符串
-- 不要在循環中拼接字符串
$ resty -e 'local begin = ngx.now()
local s = ""
-- for 循環,使用 .. 進行字符串拼接
for i = 1, 100000 dos = s .. "a"
end
ngx.update_time()
print(ngx.now() - begin)
'-- 用table進行優化
$ resty -e 'local begin = ngx.now()
local t = {}
-- for 循環,使用數組來保存字符串,自己維護數組的長度
for i = 1, 100000 dot[i] = "a"
end
local s = table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'-- 或者自己定義數組索引
$ resty -e 'local begin = ngx.now()
local t = {}
local index = 1
for i = 1, 100000 dot[index] = "a"index = index + 1
end
local response = table.concat(t, "")
ngx.say(response)
'
3、不要頻繁修改table
-- 預先創建table,然后增刪改,性能是很差的,原因在于每次新增和刪除數組元素的時候,都會涉及到數組的空間分配、resize 和 rehash。
local t = {}
local color = {first = "red", "blue", third = "green", "yellow"}-- 如果涉及對table的頻繁修改,考慮初始化table的容量,這樣就不需要使用table.insert等方法,直接通過下標設置值即可
-- table.new(narray, nhash) 兩個參數分別代表table里是array還是hash的
-- table.new(10, 0) 或者 table.new(0, 10) 這樣的,后者是 hash 性質的 table
local new_tab = require "table.new"
local t = new_tab(100, 0)
for i = 1, 100 dot[i] = i
end-- 可以考慮對table進行重用
-- 用 table.new(narray, nhash) 生了一個長度為 100 的數組,clear 后,長度還是 100。
-- table.clear方法就是將table所有的內容設為了nil
local ok, clear_tab = pcall(require, "table.clear")if not ok thenclear_tab = function (tab)for k, _ in pairs(tab) dotab[k] = nilendendend
4、不要在table中用nil
-- 一定不要在數組中使用 nil:
--No
local t = {1, 2, nil, 3}-- 如果一定要使用空值,請用 ngx.null 來表示:
--Yes
local t = {1, 2, ngx.null, 3}
5、做好異常處理
對于有錯誤信息返回的函數,我們必須對錯誤信息進行判斷和處理:
--No
local sock = ngx.socket.tcp()
local ok = sock:connect("www.baidu.com", 80)
ngx.say("successfully connected to baidu!")--Yes
local sock = ngx.socket.tcp()
localok, err = sock:connect("www.google.com", 80)
if not ok thenngx.say("failed to connect to google: ", err)return
end
ngx.say("successfully connected to google!")
而如果是自己編寫的函數,錯誤信息要作為第二個參數,用字符串的格式返回:
--No
local function foo()local ok, err = func()if not ok thenreturn falseendreturn true
end--No
local function foo()local ok, err = func()if not ok thenreturn false, {msg = err}endreturn true
end--Yes
local function foo()local ok, err = func()if not ok thenreturn false, "failed to call func(): " .. errendreturn true
end
6、ngx.var 的性能提升
ngx.var 是一個性能損耗比較大的操作,在實際使用時,我們需要用 ngx.ctx 來做一層緩存
-- lua-var-nginx-module模塊,性能比起ngx.var 提升了 5 倍。它采用的是 FFI 的方式,所以,你需要在編譯 OpenResty 的時候,先加上編譯選項
./configure --prefix=/opt/openresty \--add-module=/path/to/lua-var-nginx-module-- 然后,使用 luarocks 的方式來安裝 lua 庫:
luarocks install lua-resty-ngxvar-- 這里調用的方法也很簡單,只需要一行 fetch 函數的調用就可以了。它的效果完全等價于原有的 ngx.var.remote_addr,來獲取到終端的 IP 地址:
content_by_lua_block {local var = require("resty.ngxvar")ngx.say(var.fetch("remote_addr"))
}
三、拓展
1、加載字符串為動態方法
可以把 s 這個包含函數的字符串,改成可以由用戶指定的形式,并加上執行它的條件,這樣其實就是 FaaS 的原型了。
resty -e 'local s = [[return function()ngx.say("hello world")
end
]]
local func1 = loadstring(s)
local ret, func = pcall(func1)
func()'