OpenResty使用Lua笔记

文章目录

一、基础

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()'
相关推荐
red_redemption2 小时前
自由学习记录(24)
学习·lua
spencer_tseng2 小时前
World of Warcraft /script SetRaidTarget(“target“, n, ““) n=8,7,6,5,4,3,2,1,0
lua·wow
ross4 小时前
nginx 配置lua执行shell脚本
nginx·lua
利来利往4 小时前
lua除法bug
开发语言·bug·lua
red_redemption3 天前
自由学习记录(23)
学习·unity·lua·ab包
善良的修罗3 天前
postman 最强内置函数使用集合
测试工具·lua·postman
unix2linux4 天前
YOLO v5 Series - Image & Video Storage ( Openresty + Lua)
yolo·lua·openresty
最后一个bug4 天前
如何理解Lua 使用虚拟堆栈
linux·c语言·开发语言·嵌入式硬件·lua
半夏知半秋6 天前
lua-lru缓存算法解析
笔记·学习·算法·缓存·lua
最后一个bug6 天前
C语言执行Lua进行错误处理
linux·服务器·c语言·开发语言·嵌入式硬件·lua