lua 语法介绍与 NGINX lua 高级用法实战操作

一、概述

lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

官网:www.lua.org/

二、lua 安装

bash 复制代码
curl -L -R -O https://www.lua.org/ftp/lua-5.4.6.tar.gz
tar zxf lua-5.4.6.tar.gz
cd lua-5.4.6
make all test

测试

bash 复制代码
vim hello.lua
# 添加打印
print("Hello World!")

# 运行
lua hello.lua

命令模式

bash 复制代码
lua

三、lua 语法

1)lua 数据类型

Lua 是一种轻量级的脚本语言,具有简洁、灵活的数据类型。以下是 Lua 中的一些基本数据类型:

类型 描述 示例
nil 表示没有值或者一个无效的值。在 Lua 中,变量默认初始化为 nil。 local myVar = nil
布尔类型(boolean) 有两个值,true 和 false。 local isTrue = true local isFalse = false
数字类型(number) 表示整数或浮点数。 local integerNumber = 42 local floatNumber = 3.14
字符串类型(string) 由字符组成的序列。 字符串可以使用单引号或双引号定义。 也可以使用双方括号定义长字符串。 local myString = "Hello, Lua!" local singleQuoted = 'Single quoted string' local doubleQuoted = "Double quoted string" local longString = \[ This is a long string that spans multiple lines. ]
表(table) 类似于字典或关联数组,是 Lua 中唯一的复杂数据结构。 local myTable = {key1 = "value1", key2 = "value2"} #表中的元素可以通过键来访问。 print(myTable.key1) -- 输出: value1
函数(function) 表示一个可执行的代码块。 local function add(a, b) return a + b end local result = add(3, 4) print(result) -- 输出: 7

这些基本数据类型的组合和使用,以及 Lua 提供的灵活的表达式和控制结构,使得 Lua 成为编写脚本、配置文件、嵌入式系统等场景的理想语言。在使用 Lua 进行编程时,熟悉这些数据类型的特性和用法是很重要的。

2)lua 变量

在 Lua 中,变量是用来存储数据值的标识符。Lua 是一种动态类型的脚本语言,因此不需要显式声明变量的类型。以下是一些关于 Lua 变量的基本规则和示例:

  1. 变量命名规则 : 变量名是由字母、数字和下划线组成的字符串,不能以数字开头。Lua 是大小写敏感的,因此 myVariable 和 MyVariable 被视为不同的变量。

  2. 变量声明和赋值 : 变量可以直接赋值,不需要事先声明。如果尝试访问一个尚未赋值的变量,其值将为 nil。

bash 复制代码
myVariable = 42
anotherVariable = "Hello, Lua!"
  1. 多重赋值 : Lua 支持多重赋值,可以在一行中给多个变量赋值。
bash 复制代码
a, b, c = 1, 2, 3

此时,a 的值为 1,b 的值为 2,c 的值为 3。

  1. 全局变量和局部变量 : 默认情况下,所有变量都是全局的。如果需要创建一个局部变量,可以使用关键字 local。
bash 复制代码
local localVar = "I am local"

局部变量的作用范围限定在声明它的代码块内,而全局变量在整个程序中可见。

  1. nil 值 : 在 Lua 中,未初始化的变量的值为 nil,表示没有值或无效值。
bash 复制代码
local uninitializedVar
print(uninitializedVar)  -- 输出: nil
  1. 删除变量 : Lua 没有提供直接删除变量的语法,但可以将变量赋值为 nil,以释放其内存。
bash 复制代码
myVariable = nil

这些是 Lua 中使用变量的基本规则。在实际编程中,注意作用域、避免全局变量滥用、使用有意义的变量名等是良好的编程习惯。

3)lua 拼接字符串

在 Lua 中,可以使用不同的方法来拼接字符串。以下是一些常见的字符串拼接方法:

  1. 使用 .. 运算符 : Lua 中的字符串拼接可以使用 .. 运算符。这个运算符将两个字符串连接在一起。
bash 复制代码
local str1 = "Hello, "
local str2 = "Lua!"
local result = str1 .. str2
print(result)  -- 输出: Hello, Lua!
  1. 使用 string.format 函数 : string.format 函数可以用于格式化字符串,也可以用来拼接字符串。
bash 复制代码
local name = "John"
local age = 25
local result = string.format("My name is %s and I am %d years old.", name, age)
print(result)  -- 输出: My name is John and I am 25 years old.
  1. 使用 .. 运算符连接多个字符串 : 可以使用 .. 运算符连接多个字符串,形成更复杂的拼接。
bash 复制代码
local part1 = "Hello, "
local part2 = "how are "
local part3 = "you?"
local result = part1 .. part2 .. part3
print(result)  -- 输出: Hello, how are you?
  1. 使用表连接字符串: 将字符串存储在表中,然后使用 table.concat 函数进行连接。
bash 复制代码
local strings = {"Hello, ", "Lua!"}
local result = table.concat(strings)
print(result)  -- 输出: Hello, Lua!
  1. 使用迭代连接字符串: 可以使用迭代器将多个字符串连接起来。
bash 复制代码
local strings = {"Hello, ", "how ", "are ", "you?"}
local result = ""
for _, str in ipairs(strings) do
    result = result .. str
end
print(result)  -- 输出: Hello, how are you?

这些方法中,选择适合你需求的方式来拼接字符串。通常来说,.. 运算符是最简单和直观的方法,而 table.concat 在处理大量字符串时可能更高效。

4)lua 循环

Lua 提供了多种循环结构,可以根据不同的需求选择合适的循环类型。以下是 Lua 中常用的循环结构:

  1. while 循环 : 当给定的条件为真时,while 循环会一直执行。
bash 复制代码
local i = 1
while i <= 5 do
    print(i)
    i = i + 1
end
  1. repeat ... until 循环 : 与 while 循环类似,但条件在循环结束时进行检查。即使条件一开始就为真,循环至少会执行一次。
bash 复制代码
local i = 1
repeat
    print(i)
    i = i + 1
until i > 5
  1. for 数值型循环: 遍历数值范围内的值。
bash 复制代码
for i = 1, 5 do
    print(i)
end

这个例子会输出 1 到 5 的数字。

  1. for 泛型循环: 遍历表中的元素。
bash 复制代码
local fruits = {"apple", "banana", "orange"}
for index, value in ipairs(fruits) do
    print(index, value)
end

这个例子会输出表中的索引和对应的值。

  • for 迭代器循环: 使用自定义迭代器遍历元素。
bash 复制代码
function squares(n)
    return function()
        n = n + 1
        return n, n * n
    end
end

for i, square in squares(3) do
    print(i, square)
end

这个例子会输出从 4 开始的平方数。

  1. 循环控制语句 : Lua 提供了 break 和 goto 语句,用于在循环中进行控制流程。
bash 复制代码
for i = 1, 10 do
    if i == 5 then
        break  -- 跳出循环
    end
    print(i)
end
bash 复制代码
local i = 1
::mylabel::  -- 定义标签
if i <= 5 then
    print(i)
    i = i + 1
    goto mylabel  -- 跳转到标签
end

请注意,goto 在许多编程环境中被认为是不良实践,因为它可能导致代码不易理解和维护。尽量使用其他循环结构和控制语句来实现代码逻辑。

5)lua 函数

在 Lua 中,函数是一等公民,可以用来组织和结构化代码。以下是 Lua 中定义和使用函数的基本方法:

  1. 定义函数 : 使用 function 关键字来定义函数,后跟函数名和参数列表。函数体包含在 end 关键字之前。
bash 复制代码
function greet(name)
    print("Hello, " .. name .. "!")
end
  1. 调用函数: 使用函数名和适当的参数列表来调用函数。
bash 复制代码
greet("John")

这会输出 "Hello, John!"。

  1. 返回值 : 函数可以返回一个或多个值。使用 return 语句返回值。
bash 复制代码
function add(a, b)
    return a + b
end

local result = add(3, 4)
print(result)  -- 输出: 7
  1. 多返回值 : Lua 支持多返回值。函数可以返回多个值,用逗号分隔。
bash 复制代码
function multipleValues()
    return 1, 2, 3
end

local a, b, c = multipleValues()
print(a, b, c)  -- 输出: 1 2 3
  1. 匿名函数 : 可以使用匿名函数(也称为 lambda 函数)。
bash 复制代码
local square = function(x)
    return x * x
end

print(square(5))  -- 输出: 25

或者使用箭头函数(Lua 5.2 及以上版本):

bash 复制代码
local square = (x) => x * x
print(square(5))  -- 输出: 25
  1. 函数作为参数: 可以将函数作为参数传递给其他函数。
bash 复制代码
function apply(func, value)
    return func(value)
end

local result = apply(square, 5)
print(result)  -- 输出: 25
  1. 可变参数 : 使用 ... 表示可变参数。可以在函数内部通过 arg 表来访问这些参数。
bash 复制代码
function sum(...)
    local total = 0
    for _, value in ipairs{...} do
        total = total + value
    end
    return total
end

print(sum(1, 2, 3, 4, 5))  -- 输出: 15
  1. 闭包(Closure) : Lua 支持闭包,即在函数内部定义的函数可以访问外部函数的局部变量。
bash 复制代码
function outer()
    local x = 10
    function inner()
        return x
    end
    return inner
end

local closure = outer()
print(closure())  -- 输出: 10
  1. 尾调用优化: Lua 支持尾调用优化,即在函数的最后一个动作是调用另一个函数时,不会增加堆栈深度。这有助于避免堆栈溢出。
bash 复制代码
function factorial_tail_recursive(n, acc)
    acc = acc or 1
    if n <= 1 then
        return acc
    else
        return factorial_tail_recursive(n - 1, n * acc)
    end
end

print(factorial_tail_recursive(5))  -- 输出: 120

这些是 Lua 中函数的基本用法。函数在 Lua 中是非常灵活和强大的工具,它们支持多种编程模式,包括面向对象编程和函数式编程。

6)lua 条件控制

Lua 中的条件控制主要通过if、elseif 和 else 语句来实现。以下是 Lua 中条件控制的基本语法和示例:

  1. if 语句: 用于执行一个条件表达式,如果条件为真,则执行相应的代码块。
bash 复制代码
local x = 10

if x > 0 then
    print("x is positive")
end
  1. if-else 语句: 用于执行两个代码块中的一个,取决于条件表达式的结果。
bash 复制代码
local x = -5

if x > 0 then
    print("x is positive")
else
    print("x is non-positive")
end
  1. if-elseif-else 语句: 用于执行多个条件表达式,选择第一个为真的代码块。
bash 复制代码
local x = 0

if x > 0 then
    print("x is positive")
elseif x < 0 then
    print("x is negative")
else
    print("x is zero")
end
  1. 嵌套的 if 语句: 可以在一个 if 语句的代码块中嵌套另一个 if 语句。
bash 复制代码
local x = 10
local y = 5

if x > 0 then
    print("x is positive")
    if y > 0 then
        print("y is also positive")
    end
end
  1. 条件表达式 : 条件表达式的结果可以是任何能转换为布尔值的表达式。通常,任何非 nil 和 false 的值都被认为是真。
bash 复制代码
local x = 42

if x then
    print("x is truthy")
else
    print("x is falsy")
end
  1. 短路运算 : Lua 支持短路运算,即在逻辑表达式中使用 and 和 or 运算符时,如果确定结果的话就不再继续计算。
bash 复制代码
local x = 5
local y = 10

local result = (x > 0) and (y / x) or 0

print(result)  -- 输出: 2

这些是 Lua 中条件控制的基本用法。条件控制语句允许根据不同的条件执行不同的代码块,使得程序能够根据不同情况做出不同的决策。

7)lua 库模块

Lua 提供了一些核心的库模块,这些模块包含了一些常用的功能,以及用于处理字符串、文件、网络等任务的工具。以下是 Lua 中一些常见的库模块:

  1. string 模块: 提供了字符串处理的函数,包括拼接、查找、替换等操作。
bash 复制代码
local str1 = "Hello"
local str2 = "Lua"
local result = string.format("%s, %s!", str1, str2)
print(result)  -- 输出: Hello, Lua!
  1. table 模块: 提供了对 Lua 表(table)进行操作的函数,包括排序、连接、查找等操作。
bash 复制代码
local fruits = {"banana", "apple", "orange"}
table.sort(fruits)
print(table.concat(fruits, ", "))  -- 输出: apple, banana, orange
  1. math 模块: 提供了数学函数,如三角函数、指数函数、对数函数等。
bash 复制代码
local x = 5
local y = math.sqrt(x)
print(y)  -- 输出: 2.2360679774998
  1. io 模块: 提供了输入和输出的函数,用于读写文件、控制台等。
bash 复制代码
local file = io.open("example.txt", "w")
file:write("Hello, Lua!")
file:close()

local file = io.open("example.txt", "r")
local content = file:read("*a")
file:close()

print(content)  -- 输出: Hello, Lua!
  1. os 模块: 提供了一些与操作系统相关的函数,如执行系统命令、获取当前时间等。
bash 复制代码
local currentTime = os.time()
print(currentTime)
  1. debug 模块: 提供了一些用于调试的函数,如获取栈信息、设置断点等。
bash 复制代码
function foo()
    print(debug.traceback("Stack trace:"))
end

foo()
  1. coroutine 模块: 提供了协程(coroutine)的支持,允许非抢占式的多任务编程。
bash 复制代码
local co = coroutine.create(function()
    print("Coroutine 1")
    coroutine.yield()
    print("Coroutine 2")
end)

coroutine.resume(co)  -- 输出: Coroutine 1
coroutine.resume(co)  -- 输出: Coroutine 2
  1. package 模块: 提供了 Lua 的模块管理功能,用于加载其他 Lua 文件或库。
bash 复制代码
local myModule = require("mymodule")
myModule.myFunction()
  1. cjson 模块: cjson有两个模块:cjson和cjson.safe,前者在解析失败后会抛出异常,而后者则返回nil。

文档地址:github.com/openresty/l...

bash 复制代码
# 解析失败不会抛异常
local cjson = require "cjson.safe"
# 解析失败会抛异常
local cjson = require "cjson"

序列化

bash 复制代码
local obj = {
        code = 0,
        msg = "请求成功"
}
local json = cjson.encode(obj)

反序列化

bash 复制代码
local json = '{{"id":10001,"name":"SALSA AIR"}'
-- 反序列化
local obj = cjson.decode(json)


cjson.decode([[{"code":0,"msg":"请求成功"}]])
  1. redis 模块:openResty提供了操作Redis的模块,我们只需要引入该模块就能直接使用:
bash 复制代码
-- 引入redis模块
local redis = require('resty.redis')
-- 初始化Redis对象
local red = redis:new()
-- 设置redis超时时间
red:set_timeout(1000, 1000, 1000)

封装连接池函数:用来释放Redis连接,其实是释放连接池

bash 复制代码
-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)
    local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    if not ok then
        ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
    end
end

封装获取数据的函数

bash 复制代码
-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end

封装需要密码和db索引的函数

bash 复制代码
local function read_redis(ip, port, password, db_index, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 密码和选择的库
    red:auth(password)
    red:select(db_index)
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end

四、NGINX lua 高级用法

NGINX with Lua 是指在 NGINX 服务器上使用 Lua 编程语言进行定制化开发和扩展功能。Lua 是一种轻量级、高效的脚本语言,可以嵌入到各种应用程序和服务中。通过将 Lua 引擎集成到 NGINX 中,可以实现更灵活的配置、请求处理、身份验证等功能。

nginx 部署与介绍可以参考我以下几篇文章:

以下是一些 NGINX with Lua 的常见应用场景:

  1. 请求处理和重写: 使用 Lua 可以编写自定义的请求处理逻辑,例如 URL 重写、参数处理、请求头修改等。通过在 NGINX 配置中嵌入 Lua 脚本,可以实现更高级的请求处理。
bash 复制代码
location /example {
    content_by_lua_block {
        ngx.say("Hello from NGINX with Lua!")
    }
}
  1. 访问控制和身份验证: Lua 可以用于实现自定义的访问控制逻辑,例如基于用户、IP 地址、请求内容等的身份验证和授权。这使得可以根据具体需求定制灵活的访问策略。
bash 复制代码
location /admin {
    access_by_lua_block {
        local user = ngx.var.remote_user
        if user ~= "admin" then
            ngx.exit(ngx.HTTP_FORBIDDEN)
        end
    }
}
  1. 响应处理和过滤: Lua 可以用于在 NGINX 接收到后端服务的响应后进行进一步的处理,例如过滤响应内容、修改响应头等。
bash 复制代码
location /backend {
    proxy_pass http://backend_server;
    body_filter_by_lua_block {
        ngx.arg[1] = string.gsub(ngx.arg[1], "foo", "bar")
    }
}
  1. 动态内容生成: 使用 Lua 可以动态生成内容,例如基于后端服务的响应数据或其他外部数据源。
bash 复制代码
location /dynamic {
    content_by_lua_block {
        local args = ngx.req.get_uri_args()
        if args["enable_feature"] == "true" then
            ngx.say("Feature is enabled!")
        else
            ngx.say("Feature is disabled.")
        end
    }
}
  1. 调用外部服务 : Lua 可以通过 ngx.location.capture 等方式调用外部服务,实现与其他服务的集成。
bash 复制代码
location /api {
    content_by_lua_block {
        local res = ngx.location.capture("/external-api")
        ngx.say("Response from External API: ", res.body)
    }
}
  1. 高级请求处理: Lua 可以用于更灵活的请求处理,例如在请求处理流程中进行动态的重定向、缓存控制等。
bash 复制代码
location /dynamic_redirect {
    content_by_lua_block {
        local args = ngx.req.get_uri_args()
        if args["redirect"] == "true" then
            ngx.redirect("/new_location", ngx.HTTP_MOVED_TEMPORARILY)
        else
            ngx.say("No redirection.")
        end
    }
}
  1. 自定义日志: Lua 可以用于自定义 NGINX 的日志记录,以便记录特定信息或格式化日志输出。
bash 复制代码
http {
    log_by_lua_block {
        ngx.log(ngx.NOTICE, "Custom log message")
    }
    # 其他配置...
}
  1. 反向代理和负载均衡: 使用 Lua 可以对请求进行更复杂的负载均衡策略,例如基于请求参数、cookie 等条件进行动态的反向代理选择。
bash 复制代码
http {
    upstream backend {
        server backend1;
        server backend2;
    }

    server {
        location / {
            proxy_pass http://backend;
            proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
        }
    }
}
  1. WebSocket 支持: Lua 可以用于处理 WebSocket 连接,实现更高级的 WebSocket 功能。
bash 复制代码
location /websocket {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    content_by_lua_block {
        -- Lua WebSocket处理逻辑
    }
}
  1. 使用 Lua 第三方库: Lua 可以使用第三方库来扩展功能,例如使用 LuaRocks 安装和使用第三方模块。
bash 复制代码
http {
    lua_package_path "/path/to/lua/?.lua;;";
    init_by_lua_block {
        require("my_custom_module")
    }
    # 其他配置...
}

这些是 NGINX Lua 模块的一些高级用法。通过结合 Lua 的强大脚本能力和 NGINX 的高性能特性,可以实现各种复杂的定制化需求。注意在使用 Lua 模块时要确保版本兼容性和安全性。

【温馨提示】默认情况下 Nginx 不支持 Lua 模块,需要安装 LuaJIT 解释器,并且重新编译Nginx,或者可使用国人开发的Openrestry

lua 语法介绍与 NGINX lua 高级用法实战操作讲解就先到这里了,有任何疑问也可关注我公众号:大数据与云原生技术分享,进行技术交流,如本篇文章对您有所帮助,麻烦帮忙一键三连(点赞、转发、收藏)~

相关推荐
xixingzhe24 小时前
Nginx 看懂请求耗时瓶颈
服务器·前端·nginx
北风toto7 小时前
Windows 下用 OpenSSL 生成 SSL 密钥并配置 Nginx 教程
windows·nginx·ssl
卜锦元8 小时前
Docker Compose 部署 Nginx 1.30.6 完整指南
nginx·docker·容器
小小龙学IT11 小时前
Nginx 开源 Web 服务器与反向代理深度解
前端·nginx·开源
Coisinier1 天前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统
2301_32241428041 天前
基于web的校园失物招领管理系统59695-计算机毕设 原创(免费领源码+部署教程+开发环境)
mysql·nginx·php·apache·thinkphp·phpstudy·wampserver
flytalei1 天前
从YAML到Nginx服务访问:动手理解 Kubernetes Pod、Deployment 与 Service
运维·nginx·kubernetes
健康活着就好2 天前
# Nginx负载均衡四种策略,从原理到配置
nginx
Vitaliy Kostrov(柯维远)2 天前
谁来承担额外工作?CDN 参与者如何协商资源
nginx·缓存·系统架构·开源·cdn