Ken的杂谈
  • Ken的杂谈 (current)
  • 关于
  • 杂谈
    Java Spring Spring Boot Spring Cloud MyBatis C# .NET Core .NET ASP.NET Core ASP.NET ClassLibrary Mono 数据库 MySQL SQL Server 网络 缓存 Web Linux CentOS Ubuntu macOS Windows openEuler Nginx ServiceStack JS/JQ 正则 工具 IDE Grafana Exceptions CI/CD Solution 微服务 Arch Docker 杂谈
  • 系列
    Java 快速入门系列教程 Spring Boot 入门教程 Spring Boot 2.x 入门教程 Spring Cloud 入门教程 .NET Core 快速入门教程 ASP.NET Core 2.1 入门教程 CentOS 7 快速上手教程 Ubuntu快速上手入门教程 Hyper-V基础教程 Docker入门教程
  • GitHub

OpenResty入门:OpenResty核心机制与API

Nginx @ 2025-12-28 21:50:15 · 阅读:(6)

一、前言

OpenResty是一个基于 Nginx 与 Lua 的开源高性能 Web 平台,OpenResty团队为Nginx开发了Lua模块,使得开发者/运维可以使用Lua为OpenResty开发扩展,或者为Nginx定制功能,另外OpenResty团队也内置了很多Lua扩展(JWT、MySQL、Redis等),可以通过OpenResty高效率的开发高性能Web服务1

1、本文主要内容

  • OpenResty核心机制介绍
  • Lua 基础语法示例
  • OpenResty核心API 示例
  • OpenResty共享内存及缓存示例

2、本文环境信息

工具/环境版本说明适用版本
OSmacOS 15.7*(macOS/Windows/Linux)
DockerDocker CE 28.1.1*(默认所有Docker版本可用)
OpenResty1.27.1.21.17+

3、前置准备

1、安装OpenResty

https://ken.io/note/openresty-install-setup-linux

https://ken.io/note/openresty-install-setup-macos

https://ken.io/note/openresty-install-setup-windows

https://ken.io/note/openresty-install-setup-docker

二、OpenResty 的核心机制

OpenResty 将 Lua 虚拟机嵌入 Nginx 内核,在标准 HTTP 请求处理流程中注入了多个 Lua 执行钩子(hooks)。通过这些指令,开发者可以在不修改 C 代码的前提下,用 Lua 实现动态路由、鉴权、限流、日志增强等高级功能。

1、Nginx 的核心阶段

Nginx 处理每个请求时,会依次经过以下 11 个阶段

阶段序号阶段名称说明
1post-read读取请求头后,最早阶段(通常用于 realip 模块)
2server-rewriteserver 块内的 rewrite
3find-config根据 URI 查找 location(不可干预)
4rewrite重点:URL 改写、参数预处理
5post-rewriterewrite 后检查(如 internal redirect)
6pre-access访问控制前准备(limit_conn 等)
7access重点:权限/认证逻辑
8post-accessaccess 后处理(limit_req 等)
9try-filestry_files 指令执行
10content重点:生成响应体
11log重点:自定义日志、指标上报

2、OpenResty 的扩展阶段

阶段序号所属阶段指令典型用途
1rewrite 阶段rewrite_by_lua*URL 重写、参数校验、内部跳转
2access 阶段access_by_lua*权限控制、IP 黑名单、Token 验证
3content 阶段content_by_lua*动态生成响应内容(替代 proxy_pass / fastcgi)
4log 阶段log_by_lua*自定义日志记录、埋点统计、性能分析

✅ 后缀 * 表示支持 _block(内联 Lua 代码)和 _file(引用外部 .lua 文件)两种形式,例如:

content_by_lua_block { ngx.say("Hello") }
# 或
content_by_lua_file /path/to/handler.lua;

⚠️ 注意:Lua 代码 不能阻塞 Nginx 事件循环(避免 os.execute()、长 sleep 等),否则会拖垮整个 worker 进程。

3、指令优先级与执行顺序

在同一阶段内,原生 Nginx 指令优先于 Lua 指令执行。例如在 rewrite 阶段:

server {
    listen       8008;
    server_name  localhost;
    location / {
        default_type 'text/plain';
        content_by_lua_block {
            local args = ngx.req.get_uri_args()
            local name = args["name"] or "OpenResty test by ken.io"
            ngx.header["X-Header"] = "ken.io"
            ngx.say("Hello, " .. name .. "!")
        }
    }
    location /test {
        default_type 'text/plain';
        # 1. 原生 set
        set $flag "flag from nginx -- ken.io";
        set $flag_before "";           			
        # 2. 原生 rewrite
        rewrite ^/test$ /ken_test break;      
        # 3. Lua rewrite
        rewrite_by_lua_block {    
            ngx.var.flag_before = ngx.var.flag    	
            ngx.var.flag = "flag from OpenResty -- ken.io"
        }
        # 4. 原生 set
        set $flag "flag from nginx after -- ken.io";          
        content_by_lua_block {
            ngx.say("Nginx $flag_before: ", ngx.var.flag_before)
            ngx.say("Nginx $flag: ", ngx.var.flag)
            ngx.say("Current URI: ", ngx.var.uri)
        }
    }
}

使用 curl 命令,或者浏览器访问:http://localhost:8008 进行测试

# 使用curl访问
curl http://localhost:8008

# 输出示例
Nginx $flag_before: flag from nginx -- ken.io
Nginx $flag: flag from OpenResty -- ken.io
Current URI: /ken_test

根据运行结果可以得出,步骤 4 并未执行,且步骤 3 会覆盖步骤 1 的 flag 赋值

所以:

三、Lua 基础语法

1. 变量、表(table)、函数

-- 变量(全局/局部)
local name = "OpenResty"  -- 推荐 always use 'local'

-- 表(table = 数组 + 字典)
local user = {
    id = 1001,
    tags = {"admin", "vip"}
}
ngx.say(user.id)          --> 1001
ngx.say(#user.tags)       --> 2

-- 函数
local function greet(n)
    return "Hello, " .. n
end
ngx.say(greet(name))

2. 模块化(require 机制)

创建 lib/utils.lua:

local _M = {}

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

return _M

在 handler 中使用:

local utils = require "lib.utils"
ngx.say(utils.add(2, 3))  --> 5

✅ OpenResty 默认将 lua_package_path 指向 ?/?.lua;;,模块路径相对于 conf/ 目录。

3. 常用标准库

库名用途示例
string字符串操作string.sub("hello", 1, 3) → "hel"
table表操作table.insert(arr, val)
ngx.*OpenResty 核心 API见下文

四、OpenResty 核心API

1. ngx.var:读写 Nginx 变量

location /demo {
    set $my_var "";
    content_by_lua_block {
        ngx.var.my_var = "from lua"
        ngx.say(ngx.var.my_var)
        ngx.say(ngx.var.uri)      -- 当前 URI
    }
}

⚠️ 注意:ngx.var 性能较低,避免在高频路径中频繁读写。

2. ngx.req.*:请求信息获取与修改

-- 获取 GET/POST 参数
local args = ngx.req.get_uri_args()
local name = args.name or "guest"

-- 读取请求体(需先调用)
ngx.req.read_body()
local body = ngx.req.get_body_data()

-- 设置新 header
ngx.req.set_header("X-From-Lua", "true")

3. ngx.say / ngx.print:响应输出

ngx.say("line1")     -- 自动加 \n
ngx.print("line2")   -- 不换行

4. ngx.redirect、ngx.exit:流程控制

if not token_valid then
    ngx.redirect("/login", 302)  -- 302 跳转
end

ngx.exit(403)  -- 直接终止请求,返回 403

5. ngx.timer.*:定时任务(非阻塞)

local function timer_handler(premature, msg)
    if not premature then
        ngx.log(ngx.NOTICE, "Timer: ", msg)
    end
end

-- 5 秒后执行(仅执行一次)
local ok, err = ngx.timer.at(5, timer_handler, "hello")

✅ 定时器在独立上下文中运行,无法访问当前请求的 ngx.var 等变量。


五、共享内存与缓存

1. lua_shared_dict 使用

在 nginx.conf 的 http 块中声明共享内存区:

http {
    lua_shared_dict my_cache 10m;  # 名称 + 大小
    ...
}

在 Lua 中操作:

local cache = ngx.shared.my_cache

-- 写入(key, value, ttl秒)
cache:set("count", 1, 60)

-- 读取
local value, flags = cache:get("count")

-- 原子自增(常用于计数器)
local new_val, err = cache:incr("count", 1)

2. 实现本地缓存(计数器、令牌桶)

示例:简单限流计数器

local key = ngx.var.binary_remote_addr
local limit = 10
local req, _ = cache:get(key)

if req and req >= limit then
    ngx.exit(429)
else
    cache:incr(key, 1, 1)  -- 初始值=1,TTL=1秒
end

3. 注意事项

  • 原子性:incr()、add() 是原子操作;get()+set() 不是原子的
  • LRU 策略:当内存满时,自动淘汰最近最少使用的 key。
  • 性能:共享内存操作是 O(1),远快于 Redis 等外部存储(适合高频小数据)。

六、备注

1、开发建议

  • 阶段选择:鉴权放 access_by_lua,动态内容放 content_by_lua,日志放 log_by_lua。
  • 性能敏感:避免在 hot path 使用 ngx.var,优先用 Lua 局部变量。
  • 错误处理:Lua 异常会导致 500,务必用 pcall() 包裹关键逻辑。
  • 调试技巧:用 ngx.log(ngx.ERR, ...) 输出日志,查看 logs/error.log。

2、引用/参考

  • https://openresty.org/cn/
  • https://learn.lianglianglee.com/OpenResty

Ken的杂谈

本文由 ken.io 创作,采用CC BY 3.0 CN协议 进行许可。 可自由转载、引用、甚至修改,但需署名作者且注明出处。

Nginx

随笔目录


    © Copyright 2012-2025 Ken的杂谈

    豫ICP备10025012号-1

    ASP.NET Core(6.0) on Linux