OpenResty Web Platform 使用指南

OpenResty Web Platform Usage Guide

概述 (Overview)

OpenResty是一个基于Nginx和LuaJIT的高性能Web平台,将Nginx的强大功能与Lua脚本的灵活性完美结合。OpenResty允许开发者使用Lua语言直接在Nginx中编写复杂的业务逻辑,无需使用传统的后端应用服务器,实现高性能的Web应用、API网关和动态Web服务。

OpenResty is a high-performance web platform based on Nginx and LuaJIT, perfectly combining Nginx's powerful features with Lua scripting flexibility. OpenResty allows developers to write complex business logic directly in Nginx using Lua, eliminating the need for traditional backend application servers, enabling high-performance web applications, API gateways, and dynamic web services.

核心特性 (Core Features)

🚀 高性能 (High Performance)

  • 基于Nginx事件驱动架构
  • LuaJIT即时编译
  • 非阻塞I/O
  • 零拷贝优化
  • 超高并发处理能力

🔧 灵活可编程 (Flexible and Programmable)

  • Lua脚本动态控制
  • 热更新代码无需重启
  • 丰富的Nginx API
  • 强大的正则表达式
  • 灵活的请求处理流程

📦 丰富的模块生态 (Rich Module Ecosystem)

  • lua-resty-* 系列库
  • Redis、MySQL、HTTP客户端
  • JWT、加密、JSON处理
  • 限流、熔断、缓存
  • 第三方API集成

🌐 全栈解决方案 (Full-stack Solution)

  • Web应用服务器
  • API网关
  • 负载均衡器
  • 动态内容服务
  • 微服务网关

架构设计 (Architecture Design)

技术栈

OpenResty技术栈:
├── Nginx Core (Web服务器核心)
├── LuaJIT (Just-In-Time编译器)
├── ngx_lua模块 (Nginx-Lua集成)
├── lua-resty-* 库 (Lua生态系统)
├── 第三方Nginx模块
└── 系统库(OpenSSL、PCRE等)

请求处理流程

Nginx请求处理阶段:
1. set_by_lua* - 设置变量
2. rewrite_by_lua* - URL重写
3. access_by_lua* - 访问控制
4. content_by_lua* - 内容生成
5. header_filter_by_lua* - 响应头过滤
6. body_filter_by_lua* - 响应体过滤
7. log_by_lua* - 日志记录

安装部署 (Installation)

Ubuntu/Debian安装

# 安装依赖
sudo apt-get update
sudo apt-get install -y libpcre3-dev libssl-dev perl make build-essential curl

# 添加OpenResty仓库
wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
sudo apt-get -y install software-properties-common
sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"

# 安装OpenResty
sudo apt-get update
sudo apt-get install -y openresty

# 启动服务
sudo systemctl start openresty
sudo systemctl enable openresty

# 验证安装
openresty -v

CentOS/RHEL安装

# 添加仓库
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

# 安装
sudo yum install -y openresty

# 启动
sudo systemctl start openresty
sudo systemctl enable openresty

Docker部署

# 拉取镜像
docker pull openresty/openresty:alpine

# 运行容器
docker run -d \
  --name openresty \
  -p 80:80 \
  -p 443:443 \
  -v /path/to/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf \
  -v /path/to/lua:/usr/local/openresty/nginx/lua \
  openresty/openresty:alpine

源码编译

# 下载源码
wget https://openresty.org/download/openresty-1.21.4.1.tar.gz
tar -xzf openresty-1.21.4.1.tar.gz
cd openresty-1.21.4.1

# 配置编译选项
./configure \
  --prefix=/usr/local/openresty \
  --with-luajit \
  --with-http_ssl_module \
  --with-http_v2_module \
  --with-http_realip_module \
  --with-http_stub_status_module

# 编译安装
make -j$(nproc)
sudo make install

# 添加到PATH
export PATH=/usr/local/openresty/bin:$PATH

基础配置 (Basic Configuration)

nginx.conf示例

worker_processes auto;
error_log logs/error.log info;

events {
    worker_connections 10240;
}

http {
    include mime.types;
    default_type application/octet-stream;

    # Lua模块路径
    lua_package_path "/usr/local/openresty/nginx/lua/?.lua;;";
    lua_package_cpath "/usr/local/openresty/lualib/?.so;;";

    # 共享字典(用于缓存)
    lua_shared_dict cache 10m;
    lua_shared_dict locks 1m;

    # 初始化
    init_by_lua_block {
        require "resty.core"

        -- 全局配置
        config = {
            redis_host = "127.0.0.1",
            redis_port = 6379
        }
    }

    server {
        listen 80;
        server_name localhost;

        # 静态文件
        location /static {
            root /var/www;
            expires 7d;
        }

        # Lua处理的动态内容
        location /api {
            default_type application/json;
            content_by_lua_file /usr/local/openresty/nginx/lua/api.lua;
        }

        # Hello World示例
        location /hello {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<h1>Hello from OpenResty!</h1>")
                ngx.say("<p>Current time: ", ngx.localtime(), "</p>")
            }
        }
    }
}

Lua编程示例 (Lua Programming Examples)

1. Hello World

-- /usr/local/openresty/nginx/lua/hello.lua
local ngx = ngx

ngx.say("Hello World!")
ngx.say("Method: ", ngx.var.request_method)
ngx.say("URI: ", ngx.var.uri)
ngx.say("Args: ", ngx.var.args)
location /hello {
    content_by_lua_file /usr/local/openresty/nginx/lua/hello.lua;
}

2. RESTful API

-- api.lua
local cjson = require "cjson"
local ngx = ngx

-- 模拟数据
local users = {
    {id = 1, name = "John Doe", email = "john@example.com"},
    {id = 2, name = "Jane Smith", email = "jane@example.com"}
}

-- 路由处理
local method = ngx.var.request_method
local uri = ngx.var.uri

-- GET /api/users
if method == "GET" and uri == "/api/users" then
    ngx.say(cjson.encode({
        success = true,
        data = users
    }))
    return
end

-- GET /api/users/:id
local id = ngx.var.uri:match("/api/users/(%d+)")
if method == "GET" and id then
    for _, user in ipairs(users) do
        if user.id == tonumber(id) then
            ngx.say(cjson.encode({
                success = true,
                data = user
            }))
            return
        end
    end

    ngx.status = 404
    ngx.say(cjson.encode({
        success = false,
        error = "User not found"
    }))
    return
end

-- POST /api/users
if method == "POST" and uri == "/api/users" then
    ngx.req.read_body()
    local body = ngx.req.get_body_data()
    local data = cjson.decode(body)

    local new_user = {
        id = #users + 1,
        name = data.name,
        email = data.email
    }
    table.insert(users, new_user)

    ngx.status = 201
    ngx.say(cjson.encode({
        success = true,
        data = new_user
    }))
    return
end

-- 未匹配的路由
ngx.status = 404
ngx.say(cjson.encode({
    success = false,
    error = "Not found"
}))

3. Redis集成

-- redis_example.lua
local redis = require "resty.redis"
local cjson = require "cjson"

local red = redis:new()
red:set_timeout(1000) -- 1秒超时

-- 连接Redis
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
    ngx.log(ngx.ERR, "Failed to connect to Redis: ", err)
    ngx.status = 500
    ngx.say(cjson.encode({error = "Internal server error"}))
    return
end

-- 获取数据
local key = ngx.var.arg_key or "default"
local value, err = red:get(key)

if not value or value == ngx.null then
    -- 缓存未命中,从数据库获取
    value = "Data from database for " .. key

    -- 写入缓存
    red:setex(key, 300, value) -- 5分钟过期
end

-- 返回响应
ngx.say(cjson.encode({
    key = key,
    value = value,
    cached = value ~= ngx.null
}))

-- 连接池
red:set_keepalive(10000, 100)

4. MySQL查询

-- mysql_example.lua
local mysql = require "resty.mysql"
local cjson = require "cjson"

local db = mysql:new()
db:set_timeout(1000)

-- 连接数据库
local ok, err = db:connect({
    host = "127.0.0.1",
    port = 3306,
    database = "mydb",
    user = "root",
    password = "password"
})

if not ok then
    ngx.log(ngx.ERR, "Failed to connect to MySQL: ", err)
    ngx.status = 500
    ngx.say(cjson.encode({error = "Database connection failed"}))
    return
end

-- 查询
local res, err = db:query("SELECT * FROM users LIMIT 10")
if not res then
    ngx.log(ngx.ERR, "Query failed: ", err)
    ngx.status = 500
    ngx.say(cjson.encode({error = "Query failed"}))
    return
end

-- 返回结果
ngx.say(cjson.encode({
    success = true,
    data = res
}))

-- 连接池
db:set_keepalive(10000, 100)

5. HTTP客户端

-- http_client.lua
local http = require "resty.http"
local cjson = require "cjson"

local httpc = http.new()
httpc:set_timeout(5000)

-- 发起HTTP请求
local res, err = httpc:request_uri("https://api.github.com/users/openresty", {
    method = "GET",
    headers = {
        ["User-Agent"] = "OpenResty"
    }
})

if not res then
    ngx.log(ngx.ERR, "HTTP request failed: ", err)
    ngx.status = 500
    ngx.say(cjson.encode({error = "Request failed"}))
    return
end

-- 解析响应
local data = cjson.decode(res.body)

ngx.say(cjson.encode({
    success = true,
    status = res.status,
    data = data
}))

API网关应用 (API Gateway Application)

认证中间件

-- auth.lua
local jwt = require "resty.jwt"
local cjson = require "cjson"

local function authenticate()
    -- 获取Authorization头
    local auth_header = ngx.var.http_authorization

    if not auth_header then
        ngx.status = 401
        ngx.say(cjson.encode({error = "Missing authorization header"}))
        return ngx.exit(401)
    end

    -- 提取token
    local token = auth_header:match("Bearer%s+(.+)")
    if not token then
        ngx.status = 401
        ngx.say(cjson.encode({error = "Invalid authorization format"}))
        return ngx.exit(401)
    end

    -- 验证JWT
    local secret = "your-secret-key"
    local jwt_obj = jwt:verify(secret, token)

    if not jwt_obj.verified then
        ngx.status = 401
        ngx.say(cjson.encode({error = "Invalid token"}))
        return ngx.exit(401)
    end

    -- 将用户信息存储到ngx.ctx
    ngx.ctx.user = jwt_obj.payload
end

return {
    authenticate = authenticate
}
location /api/protected {
    access_by_lua_block {
        local auth = require "auth"
        auth.authenticate()
    }

    content_by_lua_block {
        local cjson = require "cjson"
        ngx.say(cjson.encode({
            message = "Protected resource",
            user = ngx.ctx.user
        }))
    }
}

速率限制

-- rate_limit.lua
local limit_req = require "resty.limit.req"

-- 创建限流器: 10请求/秒,允许突发20请求
local lim, err = limit_req.new("rate_limit", 10, 20)
if not lim then
    ngx.log(ngx.ERR, "Failed to create rate limiter: ", err)
    return ngx.exit(500)
end

-- 获取客户端标识(IP或用户ID)
local key = ngx.var.binary_remote_addr

-- 检查限流
local delay, err = lim:incoming(key, true)

if not delay then
    if err == "rejected" then
        ngx.status = 429
        ngx.say('{"error": "Too many requests"}')
        return ngx.exit(429)
    end

    ngx.log(ngx.ERR, "Rate limit error: ", err)
    return ngx.exit(500)
end

-- 设置延迟(如果有)
if delay >= 0.001 then
    ngx.sleep(delay)
end

负载均衡与健康检查

http {
    # 上游服务器配置
    upstream backend {
        server 192.168.1.10:8080 weight=1 max_fails=3 fail_timeout=30s;
        server 192.168.1.11:8080 weight=1 max_fails=3 fail_timeout=30s;
        server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;

        keepalive 32;
    }

    server {
        listen 80;

        location / {
            # 健康检查
            access_by_lua_block {
                local http = require "resty.http"
                local httpc = http.new()

                -- 简单的健康检查逻辑
                -- 实际生产环境应该使用专门的健康检查模块
            }

            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

服务熔断

-- circuit_breaker.lua
local shared_dict = ngx.shared.cache

local function should_open_circuit(service_name)
    local key = "circuit:" .. service_name
    local failures = shared_dict:get(key) or 0

    -- 失败次数超过阈值,开启熔断
    if failures >= 5 then
        -- 检查熔断时间
        local opened_at = shared_dict:get(key .. ":opened")
        if opened_at then
            local now = ngx.time()
            -- 熔断持续30秒
            if now - opened_at < 30 then
                return true
            else
                -- 重置熔断状态
                shared_dict:delete(key)
                shared_dict:delete(key .. ":opened")
            end
        end
    end

    return false
end

local function record_success(service_name)
    local key = "circuit:" .. service_name
    shared_dict:delete(key)
    shared_dict:delete(key .. ":opened")
end

local function record_failure(service_name)
    local key = "circuit:" .. service_name
    local failures = shared_dict:incr(key, 1, 0)

    if failures >= 5 then
        shared_dict:set(key .. ":opened", ngx.time())
        ngx.log(ngx.WARN, "Circuit breaker opened for ", service_name)
    end
end

return {
    should_open_circuit = should_open_circuit,
    record_success = record_success,
    record_failure = record_failure
}

性能优化 (Performance Optimization)

1. 共享字典缓存

-- cache.lua
local shared_dict = ngx.shared.cache

local function get_from_cache(key)
    return shared_dict:get(key)
end

local function set_to_cache(key, value, exptime)
    shared_dict:set(key, value, exptime or 300)
end

-- 使用示例
local cached_data = get_from_cache("user:123")
if not cached_data then
    -- 从数据库获取
    cached_data = fetch_from_database("123")
    set_to_cache("user:123", cached_data, 600)
end

2. LuaJIT FFI优化

-- 使用FFI调用C函数,性能提升显著
local ffi = require "ffi"

ffi.cdef[[
    int strcmp(const char *s1, const char *s2);
]]

local function fast_strcmp(s1, s2)
    return ffi.C.strcmp(s1, s2) == 0
end

3. 连接池管理

-- 所有外部连接都应该使用连接池
db:set_keepalive(10000, 100)  -- 最大空闲时间10秒,池大小100
red:set_keepalive(10000, 100)
httpc:set_keepalive(10000, 100)

监控与调试 (Monitoring and Debugging)

Prometheus监控

-- prometheus.lua
local prometheus = require "resty.prometheus"

-- 初始化(init_by_lua)
prometheus.init("prometheus_metrics")

-- 定义指标
local metric_requests = prometheus:counter(
    "nginx_http_requests_total", 
    "Number of HTTP requests", 
    {"host", "status"}
)

local metric_latency = prometheus:histogram(
    "nginx_http_request_duration_seconds",
    "HTTP request latency",
    {"host"}
)

-- 记录指标(log_by_lua)
metric_requests:inc(1, {ngx.var.host, ngx.var.status})
metric_latency:observe(ngx.now() - ngx.req.start_time(), {ngx.var.host})
location /metrics {
    content_by_lua_block {
        local prometheus = require "resty.prometheus"
        prometheus:collect()
    }
}

日志和调试

-- 不同级别的日志
ngx.log(ngx.DEBUG, "Debug message")
ngx.log(ngx.INFO, "Info message")
ngx.log(ngx.WARN, "Warning message")
ngx.log(ngx.ERR, "Error message")

-- 打印变量(开发环境)
local cjson = require "cjson"
ngx.log(ngx.DEBUG, "Data: ", cjson.encode(data))

最佳实践 (Best Practices)

1. 代码组织

/usr/local/openresty/nginx/
├── conf/
│   └── nginx.conf
├── lua/
│   ├── config.lua       # 配置
│   ├── utils.lua        # 工具函数
│   ├── middleware/      # 中间件
│   │   ├── auth.lua
│   │   ├── rate_limit.lua
│   │   └── logger.lua
│   ├── handlers/        # 请求处理器
│   │   ├── user.lua
│   │   └── order.lua
│   └── lib/            # 第三方库
└── logs/

2. 错误处理

local function safe_execute(func)
    local ok, result = pcall(func)
    if not ok then
        ngx.log(ngx.ERR, "Error: ", result)
        ngx.status = 500
        ngx.say('{"error": "Internal server error"}')
        return ngx.exit(500)
    end
    return result
end

-- 使用
safe_execute(function()
    -- 可能出错的代码
end)

3. 配置管理

-- config.lua
local _M = {}

_M.redis = {
    host = os.getenv("REDIS_HOST") or "127.0.0.1",
    port = tonumber(os.getenv("REDIS_PORT")) or 6379,
    password = os.getenv("REDIS_PASSWORD"),
    timeout = 1000,
    pool_size = 100
}

_M.mysql = {
    host = os.getenv("DB_HOST") or "127.0.0.1",
    port = tonumber(os.getenv("DB_PORT")) or 3306,
    database = os.getenv("DB_NAME") or "mydb",
    user = os.getenv("DB_USER") or "root",
    password = os.getenv("DB_PASSWORD")
}

return _M

与其他方案对比 (Comparison)

特性 OpenResty Nginx Nginx + Python/Node.js
性能 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐
开发灵活性 ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
学习曲线 中等 中等
生态系统 丰富 有限 非常丰富
部署复杂度 中高
内存占用 极低 中高

使用场景 (Use Cases)

✅ 理想场景

  • 高性能API网关
  • 动态Web服务
  • 反向代理和负载均衡
  • WAF(Web应用防火墙)
  • CDN边缘节点
  • 微服务网关
  • 实时数据处理

⚠️ 考虑其他方案

  • 复杂的业务逻辑(考虑传统后端)
  • 团队不熟悉Lua(考虑Node.js/Go)
  • 需要丰富的框架支持
  • CPU密集型任务

学习资源 (Learning Resources)

总结 (Summary)

OpenResty是一个强大的Web平台,特别适合:

主要优势

  • 🚀 极致性能(Nginx + LuaJIT)
  • 🔧 灵活可编程(Lua脚本)
  • 📦 丰富的lua-resty库
  • ⚡ 低延迟、高并发
  • 🌐 完整的API网关解决方案

典型应用

  1. API网关: 认证、限流、熔断、路由
  2. 动态Web服务: 无需额外应用服务器
  3. 边缘计算: CDN节点动态逻辑
  4. WAF: Web应用防护
  5. 微服务网关: 服务发现、负载均衡

OpenResty将Nginx的高性能与Lua的灵活性完美结合,是构建现代高性能Web应用和API网关的优秀选择。


最后更新: 2025年11月

Last Updated: November 2025

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-11-25 18:08:14

results matching ""

    No results matching ""