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()'
相关推荐
程序猿多布6 小时前
XLua教程之Lua调用C#
unity·c#·lua·xlua
夜猫逐梦18 小时前
【Lua】Windows 下编写 C 扩展模块:VS 编译与 Lua 调用全流程
c语言·windows·lua
java搬砖工-苤-初心不变1 天前
OpenResty 配合 Lua 脚本的使用
开发语言·lua·openresty
半夏知半秋2 天前
基于跳跃表的zset实现解析(lua版)
服务器·开发语言·redis·学习·lua
Wyc724092 天前
Lua语言基础笔记
开发语言·笔记·lua
观测云3 天前
Openresty Tracing 最佳实践
openresty
锐策5 天前
Lua 核心知识点详解
开发语言·lua
lanhuazui105 天前
lua中 string.match返回值
lua
无名客05 天前
redis分布式锁为什么采用Lua脚本实现。而不是事务
redis·分布式·lua·事务
我发在否6 天前
Lua > OpenResty HelloWorld
lua·openresty