文章目录
- 一、基础
- 二、性能提升
-
- 1、使用fft调用shell
- 2、不要在循环中拼接字符串
- 3、不要频繁修改table
- 4、不要在table中用nil
- 5、做好异常处理
- [6、ngx.var 的性能提升](#6、ngx.var 的性能提升)
- 三、拓展
一、基础
1、常用
OpenResty 中文官网:https://openresty.org/cn/
包管理:https://opm.openresty.org/
(1)Lua 的下标从 1 开始
(2)使用 ... 来拼接字符串
2、使用局部变量
在 Lua 中,变量默认是全局的,会被放到名为 _G 的 table 中
。不加 local 的变量会在全局表中查找,这是昂贵的操作。如果再加上一些变量名的拼写错误,就会造成难以定位的 bug。
建议总是使用 local 来声明变量,即使在 require module 的时候也是一样:
lua
-- Recommended
local xxx = require('xxx')
-- Avoid
require('xxx')
local ngx_re = require "ngx.re"
local ngx = require "ngx"
lua
--No
local function foo()
local ok, err = ngx.timer.at(delay, handler)
end
--Yes
local timer_at = ngx.timer.at
local function foo()
local ok, err = timer_at(delay, handler)
end
lua
-- 为了风格的统一,require 和 ngx 也需要 local 化:
--No
local core = require("apisix.core")
local timer_at = ngx.timer.at
local 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.at
local function foo()
local ok, err = timer_at(delay, handler)
end
3、模块化
lua
-- hello.lua定义一个对象,并返回
local _M = {}
_M.color = {
red = 1,
blue = 2,
green = 3
}
return _M
lua
location / {
content_by_lua_block {
-- 引入hello,并用一个变量接收,这个变量就可以调用对象的方法和变量了
local hello = require "hello"
ngx.say(hello.color.green)
}
}
二、性能提升
1、使用fft调用shell
lua
-- 不要用阻塞的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、不要在循环中拼接字符串
lua
-- 不要在循环中拼接字符串
$ resty -e 'local begin = ngx.now()
local s = ""
-- for 循环,使用 .. 进行字符串拼接
for i = 1, 100000 do
s = s .. "a"
end
ngx.update_time()
print(ngx.now() - begin)
'
-- 用table进行优化
$ resty -e 'local begin = ngx.now()
local t = {}
-- for 循环,使用数组来保存字符串,自己维护数组的长度
for i = 1, 100000 do
t[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 do
t[index] = "a"
index = index + 1
end
local response = table.concat(t, "")
ngx.say(response)
'
3、不要频繁修改table
lua
-- 预先创建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 do
t[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 then
clear_tab = function (tab)
for k, _ in pairs(tab) do
tab[k] = nil
end
end
end
4、不要在table中用nil
lua
-- 一定不要在数组中使用 nil:
--No
local t = {1, 2, nil, 3}
-- 如果一定要使用空值,请用 ngx.null 来表示:
--Yes
local t = {1, 2, ngx.null, 3}
5、做好异常处理
对于有错误信息返回的函数,我们必须对错误信息进行判断和处理:
lua
--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 then
ngx.say("failed to connect to google: ", err)
return
end
ngx.say("successfully connected to google!")
而如果是自己编写的函数,错误信息要作为第二个参数,用字符串的格式返回:
lua
--No
local function foo()
local ok, err = func()
if not ok then
return false
end
return true
end
--No
local function foo()
local ok, err = func()
if not ok then
return false, {msg = err}
end
return true
end
--Yes
local function foo()
local ok, err = func()
if not ok then
return false, "failed to call func(): " .. err
end
return true
end
6、ngx.var 的性能提升
ngx.var 是一个性能损耗比较大的操作,在实际使用时,我们需要用 ngx.ctx 来做一层缓存
lua
-- 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 的原型了。
lua
resty -e 'local s = [[
return function()
ngx.say("hello world")
end
]]
local func1 = loadstring(s)
local ret, func = pcall(func1)
func()'