1. Lua语言简介

1.1 什么是Lua

Lua是一种轻量级、高级的动态编程语言,专为嵌入应用程序而设计。在OpenResty中,Lua作为脚本语言提供了强大的可编程能力,使得我们可以在Nginx中实现复杂的业务逻辑。

1.2 Lua的特点

  • 轻量级:核心库小于200KB
  • 高性能:通过LuaJIT实现接近C语言的性能
  • 可嵌入:易于集成到其他应用程序中
  • 动态类型:运行时确定变量类型
  • 垃圾回收:自动内存管理
  • 协程支持:内置协程机制

1.3 在OpenResty中的作用

  • 请求处理逻辑
  • 数据处理和转换
  • 外部服务调用
  • 缓存操作
  • 认证和授权

2. 基本语法

2.1 注释

-- 单行注释

--[[
多行注释
可以跨越多行
--]]

--[=[
嵌套多行注释
--[[
内部注释
--]]
--]=]

2.2 变量和标识符

-- 变量声明(全局变量)
name = "OpenResty"
age = 10

-- 局部变量
local local_name = "Local Variable"
local local_age = 5

-- 多重赋值
local a, b, c = 1, 2, 3
local x, y = 10  -- y为nil

-- 变量交换
a, b = b, a

2.3 数据类型

-- nil(空值)
local empty_var = nil

-- boolean(布尔值)
local is_true = true
local is_false = false

-- number(数字)
local integer = 42
local float = 3.14159
local scientific = 1.23e-4

-- string(字符串)
local str1 = "Hello"
local str2 = 'World'
local str3 = [[多行
字符串]]

-- table(表)
local array = {1, 2, 3, 4, 5}
local dict = {name = "OpenResty", version = "1.21"}

-- function(函数)
local func = function(x) return x * 2 end

-- userdata(用户数据)
-- thread(线程/协程)

2.4 类型检查

local value = "Hello"
print(type(value))  -- string

value = 42
print(type(value))  -- number

value = {1, 2, 3}
print(type(value))  -- table

3. 运算符

3.1 算术运算符

local a, b = 10, 3

print(a + b)   -- 13 (加法)
print(a - b)   -- 7  (减法)
print(a * b)   -- 30 (乘法)
print(a / b)   -- 3.333... (除法)
print(a % b)   -- 1  (取模)
print(a ^ b)   -- 1000 (幂运算)
print(-a)      -- -10 (负号)

3.2 关系运算符

local a, b = 10, 5

print(a == b)  -- false (等于)
print(a ~= b)  -- true  (不等于)
print(a > b)   -- true  (大于)
print(a < b)   -- false (小于)
print(a >= b)  -- true  (大于等于)
print(a <= b)  -- false (小于等于)

3.3 逻辑运算符

local a, b = true, false

print(a and b)  -- false (逻辑与)
print(a or b)   -- true  (逻辑或)
print(not a)    -- false (逻辑非)

-- 短路求值
local result = a and "true value" or "false value"
print(result)   -- "true value"

3.4 其他运算符

-- 字符串连接
local str = "Hello" .. " " .. "World"
print(str)  -- "Hello World"

-- 长度运算符
local arr = {1, 2, 3, 4, 5}
print(#arr)  -- 5

local str = "Hello"
print(#str)  -- 5

4. 控制结构

4.1 条件语句

-- if语句
local score = 85

if score >= 90 then
    print("优秀")
elseif score >= 80 then
    print("良好")
elseif score >= 60 then
    print("及格")
else
    print("不及格")
end

-- 三元运算符模拟
local result = score >= 60 and "及格" or "不及格"
print(result)

4.2 循环语句

-- while循环
local i = 1
while i <= 5 do
    print("while: " .. i)
    i = i + 1
end

-- repeat-until循环
local j = 1
repeat
    print("repeat: " .. j)
    j = j + 1
until j > 5

-- for数值循环
for k = 1, 5 do
    print("for: " .. k)
end

-- for步长循环
for k = 1, 10, 2 do
    print("step: " .. k)  -- 1, 3, 5, 7, 9
end

-- for泛型循环
local arr = {"a", "b", "c"}
for index, value in ipairs(arr) do
    print(index, value)
end

local dict = {name = "OpenResty", version = "1.21"}
for key, value in pairs(dict) do
    print(key, value)
end

4.3 跳转语句

-- break语句
for i = 1, 10 do
    if i == 5 then
        break
    end
    print(i)
end

-- goto语句(Lua 5.2+)
for i = 1, 10 do
    if i == 5 then
        goto continue
    end
    print(i)
    ::continue::
end

5. 函数

5.1 函数定义

-- 基本函数定义
function greet(name)
    return "Hello, " .. name
end

-- 匿名函数
local add = function(a, b)
    return a + b
end

-- 局部函数
local function multiply(a, b)
    return a * b
end

-- 调用函数
print(greet("OpenResty"))  -- Hello, OpenResty
print(add(3, 4))           -- 7
print(multiply(5, 6))      -- 30

5.2 多返回值

function get_name_age()
    return "Alice", 25
end

-- 接收多个返回值
local name, age = get_name_age()
print(name, age)  -- Alice 25

-- 只接收第一个返回值
local first = get_name_age()
print(first)  -- Alice

5.3 可变参数

function sum(...)
    local args = {...}
    local total = 0
    for i = 1, #args do
        total = total + args[i]
    end
    return total
end

print(sum(1, 2, 3, 4, 5))  -- 15

-- 使用select函数
function print_args(...)
    local n = select('#', ...)
    for i = 1, n do
        local arg = select(i, ...)
        print("Arg " .. i .. ": " .. tostring(arg))
    end
end

print_args("a", "b", "c")

5.4 闭包

function create_counter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end

local counter1 = create_counter()
local counter2 = create_counter()

print(counter1())  -- 1
print(counter1())  -- 2
print(counter2())  -- 1
print(counter1())  -- 3

6. 表(Table)

6.1 表的创建

-- 空表
local empty_table = {}

-- 数组风格
local array = {"apple", "banana", "orange"}

-- 字典风格
local person = {
    name = "Alice",
    age = 30,
    city = "Beijing"
}

-- 混合风格
local mixed = {
    "first",
    "second",
    name = "mixed table",
    ["key with spaces"] = "value"
}

6.2 表的访问

local person = {name = "Alice", age = 30}

-- 点号访问
print(person.name)  -- Alice

-- 方括号访问
print(person["age"])  -- 30

-- 动态键访问
local key = "name"
print(person[key])  -- Alice

-- 数组访问
local arr = {10, 20, 30}
print(arr[1])  -- 10 (Lua数组从1开始)
print(arr[2])  -- 20

6.3 表的修改

local person = {name = "Alice"}

-- 添加新字段
person.age = 30
person["city"] = "Beijing"

-- 修改现有字段
person.name = "Bob"

-- 删除字段
person.age = nil

print(person.name)  -- Bob
print(person.age)   -- nil
print(person.city)  -- Beijing

6.4 表的遍历

local person = {name = "Alice", age = 30, city = "Beijing"}

-- pairs遍历所有键值对
for key, value in pairs(person) do
    print(key, value)
end

-- ipairs遍历数组部分
local arr = {"a", "b", "c"}
for index, value in ipairs(arr) do
    print(index, value)
end

-- 数值for循环遍历数组
for i = 1, #arr do
    print(i, arr[i])
end

7. 字符串处理

7.1 字符串基本操作

local str = "Hello, OpenResty!"

-- 字符串长度
print(#str)  -- 17
print(string.len(str))  -- 17

-- 字符串连接
local new_str = str .. " Welcome!"
print(new_str)

-- 字符串重复
local repeated = string.rep("Ha", 3)
print(repeated)  -- HaHaHa

7.2 字符串查找和替换

local text = "Hello, World! Hello, Lua!"

-- 查找子字符串
local start, end_pos = string.find(text, "World")
print(start, end_pos)  -- 8 12

-- 模式匹配
local match = string.match(text, "H(%w+)")
print(match)  -- ello

-- 全局匹配
for word in string.gmatch(text, "%w+") do
    print(word)
end

-- 字符串替换
local new_text = string.gsub(text, "Hello", "Hi")
print(new_text)  -- Hi, World! Hi, Lua!

7.3 字符串格式化

-- 格式化字符串
local name = "Alice"
local age = 30
local formatted = string.format("Name: %s, Age: %d", name, age)
print(formatted)  -- Name: Alice, Age: 30

-- 数字格式化
local pi = 3.14159
print(string.format("%.2f", pi))  -- 3.14
print(string.format("%08.2f", pi))  -- 00003.14

7.4 字符串转换

-- 大小写转换
local text = "Hello World"
print(string.upper(text))  -- HELLO WORLD
print(string.lower(text))  -- hello world

-- 字符串反转
print(string.reverse(text))  -- dlroW olleH

-- 子字符串
print(string.sub(text, 1, 5))   -- Hello
print(string.sub(text, 7))      -- World
print(string.sub(text, -5))     -- World

8. 错误处理

8.1 pcall和xpcall

-- pcall保护调用
function risky_function(x)
    if x == 0 then
        error("Division by zero!")
    end
    return 10 / x
end

-- 使用pcall
local success, result = pcall(risky_function, 5)
if success then
    print("Result: " .. result)  -- Result: 2
else
    print("Error: " .. result)
end

-- 错误情况
local success, result = pcall(risky_function, 0)
if success then
    print("Result: " .. result)
else
    print("Error: " .. result)  -- Error: Division by zero!
end

8.2 自定义错误处理

-- 错误处理函数
function error_handler(err)
    return "Custom error: " .. tostring(err)
end

-- 使用xpcall
local success, result = xpcall(risky_function, error_handler, 0)
if not success then
    print(result)  -- Custom error: Division by zero!
end

9. 模块和包

9.1 创建模块

-- math_utils.lua
local M = {}

function M.add(a, b)
    return a + b
end

function M.multiply(a, b)
    return a * b
end

M.PI = 3.14159

return M

9.2 使用模块

-- 加载模块
local math_utils = require("math_utils")

-- 使用模块函数
print(math_utils.add(3, 4))      -- 7
print(math_utils.multiply(5, 6)) -- 30
print(math_utils.PI)             -- 3.14159

10. 协程(Coroutine)

10.1 协程基础

-- 创建协程
local co = coroutine.create(function()
    for i = 1, 3 do
        print("Coroutine: " .. i)
        coroutine.yield()
    end
end)

-- 运行协程
print(coroutine.status(co))  -- suspended
coroutine.resume(co)         -- Coroutine: 1
print(coroutine.status(co))  -- suspended
coroutine.resume(co)         -- Coroutine: 2
coroutine.resume(co)         -- Coroutine: 3
print(coroutine.status(co))  -- dead

10.2 协程通信

local co = coroutine.create(function(a, b)
    print("Received:", a, b)
    local x, y = coroutine.yield(a + b)
    print("Received:", x, y)
    return x * y
end)

local success, result = coroutine.resume(co, 10, 20)
print("Yielded:", result)  -- Yielded: 30

local success, result = coroutine.resume(co, 5, 6)
print("Returned:", result)  -- Returned: 30

11. 在OpenResty中的应用

11.1 基本示例

-- 在nginx.conf中
location /lua-demo {
    content_by_lua_block {
        local name = ngx.var.arg_name or "World"
        ngx.say("Hello, " .. name .. "!")
        
        local data = {
            message = "Welcome to OpenResty",
            timestamp = ngx.time(),
            user = name
        }
        
        ngx.header.content_type = "application/json"
        ngx.say(require("cjson").encode(data))
    }
}

11.2 实用函数

-- 工具函数模块
local _M = {}

-- 生成随机字符串
function _M.random_string(length)
    local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    local result = {}
    for i = 1, length do
        local index = math.random(1, #chars)
        result[i] = string.sub(chars, index, index)
    end
    return table.concat(result)
end

-- 验证邮箱格式
function _M.is_valid_email(email)
    local pattern = "^[%w%._%+%-]+@[%w%._%+%-]+%.%w+$"
    return string.match(email, pattern) ~= nil
end

-- 深拷贝表
function _M.deep_copy(orig)
    local copy
    if type(orig) == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[_M.deep_copy(orig_key)] = _M.deep_copy(orig_value)
        end
        setmetatable(copy, _M.deep_copy(getmetatable(orig)))
    else
        copy = orig
    end
    return copy
end

return _M

12. 总结

Lua作为OpenResty的核心脚本语言,提供了强大而灵活的编程能力。掌握Lua的基础语法、数据类型、控制结构、函数、表操作等核心概念,是深入学习OpenResty的重要基础。通过本章的学习,我们为后续的OpenResty开发打下了坚实的语言基础。

13. 参考资料