强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

OpenResty 高性能网关开发教程 / 第 16 章 - 最佳实践与生产部署

第 16 章 - 最佳实践与生产部署

16.1 项目结构

16.1.1 推荐目录结构

openresty-gateway/
├── docker/
│   ├── Dockerfile
│   ├── Dockerfile.dev
│   └── docker-entrypoint.sh
├── nginx/
│   └── conf/
│       ├── nginx.conf              # 主配置
│       ├── conf.d/
│       │   ├── upstream.conf       # 上游服务
│       │   ├── location.conf       # 路由规则
│       │   └── security.conf       # 安全配置
│       └── ssl/
│           ├── cert.pem
│           └── key.pem
├── lua/
│   ├── init.lua                    # 初始化代码
│   ├── init_worker.lua             # Worker 初始化
│   ├── access.lua                  # 访问控制
│   ├── rewrite.lua                 # URL 改写
│   ├── content/
│   │   ├── health.lua
│   │   └── admin.lua
│   ├── auth/
│   │   ├── jwt_auth.lua
│   │   ├── apikey_auth.lua
│   │   └── rbac.lua
│   ├── balancer/
│   │   └── round_robin.lua
│   ├── cache/
│   │   ├── response_cache.lua
│   │   └── lru_cache.lua
│   ├── limiters/
│   │   ├── token_bucket.lua
│   │   ├── sliding_window.lua
│   │   └── distributed.lua
│   ├── router/
│   │   ├── trie_router.lua
│   │   └── version_router.lua
│   ├── security/
│   │   ├── waf.lua
│   │   ├── sql_injection.lua
│   │   └── xss_protection.lua
│   ├── transform/
│   │   ├── body_transform.lua
│   │   └── header_transform.lua
│   ├── utils/
│   │   ├── logger.lua
│   │   └── http_client.lua
│   └── plugins/
│       ├── base_plugin.lua
│       └── rate_limit.lua
├── tests/
│   ├── unit/
│   ├── integration/
│   └── benchmark/
├── scripts/
│   ├── benchmark.sh
│   └── deploy.sh
├── docker-compose.yml
├── Makefile
└── README.md

16.1.2 配置分层

# nginx.conf - 主配置(不频繁修改)
worker_processes auto;
error_log /var/log/openresty/error.log warn;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    include mime.types;

    # 基础配置
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;

    # Lua 配置
    lua_package_path "/usr/local/openresty/lua/?.lua;/usr/local/openresty/lua/?/init.lua;;";
    lua_package_cpath "/usr/local/openresty/lua/?.so;;";
    lua_code_cache on;  # 生产环境必须开启

    # 共享内存
    lua_shared_dict gateway_config 10m;
    lua_shared_dict rate_limit     50m;
    lua_shared_dict jwt_cache      20m;
    lua_shared_dict service_cache  20m;
    lua_shared_dict cache_data     100m;

    # 初始化
    init_by_lua_file    lua/init.lua;
    init_worker_by_lua_file lua/init_worker.lua;

    # 引入站点配置
    include conf.d/*.conf;
}
# conf.d/upstream.conf - 上游服务(按服务变更)
upstream user_service {
    least_conn;
    server 10.0.1.1:8080 weight=5;
    server 10.0.1.2:8080 weight=5;
    keepalive 32;
}

upstream order_service {
    least_conn;
    server 10.0.2.1:8080 weight=5;
    server 10.0.2.2:8080 weight=5;
    keepalive 32;
}
# conf.d/location.conf - 路由规则(频繁变更)
server {
    listen 8080;

    # 通用变量
    set $user_id "";
    set $user_role "";
    set $request_id $request_id;

    # WAF 防护
    access_by_lua_file lua/access.lua;

    # 安全头
    header_filter_by_lua_block {
        ngx.header["X-Content-Type-Options"] = "nosniff"
        ngx.header["X-Frame-Options"] = "DENY"
        ngx.header["X-Request-ID"] = ngx.var.request_id
    }

    # 日志
    log_by_lua_file lua/log.lua;

    # 路由
    location /api/users {
        proxy_pass http://user_service;
    }

    location /api/orders {
        proxy_pass http://order_service;
    }
}

16.2 性能调优

16.2.1 Nginx 配置优化

# 系统级优化
worker_processes auto;                    # 自动匹配 CPU 核数
worker_rlimit_nofile 65535;              # 文件描述符限制

events {
    worker_connections 16384;             # 每个 Worker 最大连接数
    use epoll;                           # Linux 高性能事件模型
    multi_accept on;                     # 一次接受多个连接
    accept_mutex off;                    # 高并发时关闭
}

http {
    # 缓冲区优化
    client_body_buffer_size 16k;
    client_header_buffer_size 1k;
    client_max_body_size 10m;
    large_client_header_buffers 4 8k;

    # 超时优化
    keepalive_timeout 65;
    keepalive_requests 1000;
    client_body_timeout 12;
    client_header_timeout 12;
    send_timeout 10;

    # 压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_types
        application/javascript
        application/json
        application/xml
        text/css
        text/plain
        text/xml;

    # 连接池
    upstream backend {
        server 10.0.1.1:8080;
        keepalive 64;                    # 保持 64 个空闲连接
        keepalive_requests 1000;         # 每个连接处理 1000 请求
        keepalive_timeout 60s;
    }
}

16.2.2 Lua 代码优化

-- 优化 1:使用局部变量
local ngx_var = ngx.var
local ngx_req = ngx.req
local ngx_log = ngx.log
local ngx_ERR = ngx.ERR

-- 优化 2:预编译正则
local compiled_patterns = {}
for _, pattern in ipairs(patterns) do
    compiled_patterns[pattern] = ngx.re.compile(pattern, "joi")
end

-- 优化 3:表预分配
local results = new_tab(100, 0)  -- LuaJIT 优化

-- 优化 4:避免在热路径中创建闭包
-- ❌ 每次请求都创建闭包
local function handle_request()
    local callback = function()
        -- 处理逻辑
    end
    callback()
end

-- ✅ 复用已有函数
local function callback()
    -- 处理逻辑
end
local function handle_request()
    callback()
end

-- 优化 5:使用 FFI 替代纯 Lua
local ffi = require "ffi"
ffi.cdef[[
    unsigned long long strtoull(const char *nptr, char **endptr, int base);
]]

-- 优化 6:批量操作 Redis
-- ❌ 逐个操作
for _, key in ipairs(keys) do
    red:get(key)
end

-- ✅ Pipeline 批量操作
red:init_pipeline()
for _, key in ipairs(keys) do
    red:get(key)
end
local results = red:commit_pipeline()

16.2.3 系统调优

# /etc/sysctl.conf

# 文件描述符
fs.file-max = 1000000
fs.nr_open = 1000000

# 网络优化
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10

# 缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 应用配置
sysctl -p
# /etc/security/limits.conf
*               soft    nofile          65535
*               hard    nofile          65535
*               soft    nproc           65535
*               hard    nproc           65535

16.3 插件开发

16.3.1 插件接口定义

-- /usr/local/openresty/lua/plugins/base_plugin.lua
local _M = {}
_M.__index = _M

-- 插件元信息
_M.name = "base"
_M.version = "1.0.0"
_M.priority = 0  -- 优先级(越大越先执行)
_M.description = "Base plugin class"

-- 生命周期钩子
function _M:init()
    -- Master 进程初始化
end

function _M:init_worker()
    -- Worker 进程初始化
end

function _M:rewrite(ctx)
    -- rewrite 阶段
    return true
end

function _M:access(ctx)
    -- access 阶段
    return true
end

function _M:header_filter(ctx)
    -- 响应头过滤
end

function _M:body_filter(ctx)
    -- 响应体过滤
end

function _M:log(ctx)
    -- 日志阶段
end

function _M:destroy()
    -- 清理资源
end

-- 构造函数
function _M.new(config)
    local self = setmetatable({}, _M)
    self.config = config or {}
    self.enabled = true
    return self
end

return _M

16.3.2 示例插件:请求 ID

-- /usr/local/openresty/lua/plugins/request_id.lua
local base = require "plugins.base_plugin"
local _M = setmetatable({}, {__index = base})
_M.__index = _M

_M.name = "request-id"
_M.version = "1.0.0"
_M.priority = 1000  -- 高优先级,最先执行

local random = math.random

-- 生成唯一请求 ID
local function generate_id()
    return string.format("%08x%04x%04x%04x%012x",
        ngx.now() * 1000,
        random(0, 0xffff),
        random(0, 0xffff),
        random(0, 0xffff),
        random(0, 0xffffffffffff))
end

function _M.new(config)
    local self = base.new(config)
    setmetatable(self, _M)
    self.header_name = config.header_name or "X-Request-ID"
    return self
end

function _M:rewrite(ctx)
    -- 检查是否已有请求 ID(来自上游)
    local request_id = ngx.req.get_headers()[self.header_name]
    if not request_id then
        request_id = generate_id()
    end

    -- 设置到变量和上下文
    ngx.var.request_id = request_id
    ctx.request_id = request_id

    -- 传递给后端
    ngx.req.set_header(self.header_name, request_id)

    return true
end

function _M:header_filter(ctx)
    -- 返回给客户端
    ngx.header[self.header_name] = ctx.request_id
end

return _M

16.3.3 插件加载器

-- /usr/local/openresty/lua/plugins/loader.lua
local _M = {}

local cjson = require "cjson"

-- 已注册的插件
local plugins = {}

-- 注册插件
function _M.register(plugin_class, config)
    local plugin = plugin_class.new(config)
    table.insert(plugins, plugin)

    -- 按优先级排序
    table.sort(plugins, function(a, b)
        return (a.priority or 0) > (b.priority or 0)
    end)

    return plugin
end

-- 执行指定阶段的所有插件
function _M.execute_phase(phase, ctx)
    for _, plugin in ipairs(plugins) do
        if plugin.enabled and plugin[phase] then
            local ok, result = pcall(plugin[phase], plugin, ctx)
            if not ok then
                ngx.log(ngx.ERR, "Plugin ", plugin.name, " error in ", phase, ": ", result)
                -- 根据配置决定是否阻断
                if plugin.config.critical then
                    return false
                end
            elseif result == false then
                -- 插件主动阻断请求
                return false
            end
        end
    end
    return true
end

-- 获取插件列表
function _M.list()
    local result = {}
    for _, plugin in ipairs(plugins) do
        table.insert(result, {
            name = plugin.name,
            version = plugin.version,
            priority = plugin.priority,
            enabled = plugin.enabled,
        })
    end
    return result
end

return _M

16.3.4 插件化 nginx 配置

server {
    listen 8080;

    rewrite_by_lua_block {
        local loader = require "plugins.loader"
        local ctx = {}
        ngx.ctx.plugin_ctx = ctx
        if not loader.execute_phase("rewrite", ctx) then
            return ngx.exit(ngx.HTTP_FORBIDDEN)
        end
    }

    access_by_lua_block {
        local loader = require "plugins.loader"
        local ctx = ngx.ctx.plugin_ctx
        if not loader.execute_phase("access", ctx) then
            return ngx.exit(ngx.HTTP_FORBIDDEN)
        end
    }

    header_filter_by_lua_block {
        local loader = require "plugins.loader"
        loader.execute_phase("header_filter", ngx.ctx.plugin_ctx)
    }

    log_by_lua_block {
        local loader = require "plugins.loader"
        loader.execute_phase("log", ngx.ctx.plugin_ctx)
    }
}

16.3.5 插件初始化

-- lua/init_worker.lua

local loader = require "plugins.loader"

-- 注册内置插件
loader.register(require "plugins.request_id", {})
loader.register(require "plugins.rate_limit", {
    max_requests = 1000,
    window = 60,
})
loader.register(require "plugins.jwt_auth", {
    secret = os.getenv("JWT_SECRET"),
})

-- 注册自定义插件
loader.register(require "plugins.my_custom_plugin", {
    enabled = true,
    some_config = "value",
})

ngx.log(ngx.INFO, "Loaded ", #loader.list(), " plugins")

16.4 生产部署清单

16.4.1 部署前检查

类别 检查项 状态
安全 JWT Secret 已更换为随机值 □
安全 已启用 HTTPS □
安全 已配置 WAF 规则 □
安全 已设置 IP 黑名单 □
安全 日志中无敏感信息 □
性能 已开启 Lua 代码缓存 □
性能 已配置连接池 □
性能 已设置合理的超时 □
监控 Prometheus 指标已暴露 □
监控 健康检查端点已配置 □
监控 告警规则已配置 □
容灾 已配置限流 □
容灾 已配置熔断器 □
容灾 已配置降级策略 □
运维 日志轮转已配置 □
运维 配置已版本控制 □
测试 单元测试通过 □
测试 集成测试通过 □
测试 压力测试完成 □

16.4.2 nginx.conf 生产配置检查

# ⚠️ 生产环境必须确认的配置

http {
    # 1. 必须开启 Lua 代码缓存
    lua_code_cache on;

    # 2. 错误日志级别(不要用 debug)
    error_log /var/log/openresty/error.log warn;

    # 3. 关闭 stub_status(或限制访问)
    location /nginx_status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }

    # 4. 隐藏服务器信息
    server_tokens off;
    more_clear_headers Server;

    # 5. 限制请求体大小
    client_max_body_size 10m;
}

16.5 故障排查

16.5.1 常见问题

问题 可能原因 排查方法
502 Bad Gateway 后端服务不可用 检查 upstream 日志、健康检查
504 Gateway Timeout 后端响应超时 调整 proxy_read_timeout
429 Too Many Requests 触发限流 检查限流配置和日志
内存持续增长 Lua 代码内存泄漏 使用 collectgarbage("count") 监控
Worker 重启 OOM 或段错误 检查 dmesg 和 error.log
连接池耗尽 未正确释放连接 检查 set_keepalive 调用

16.5.2 调试工具

-- 1. 请求信息 dump
local function dump_request()
    ngx.log(ngx.INFO, "=== Request Info ===")
    ngx.log(ngx.INFO, "Method: ", ngx.req.get_method())
    ngx.log(ngx.INFO, "URI: ", ngx.var.uri)
    ngx.log(ngx.INFO, "Args: ", ngx.var.args)
    ngx.log(ngx.INFO, "Headers: ", cjson.encode(ngx.req.get_headers()))
    ngx.log(ngx.INFO, "Client IP: ", ngx.var.remote_addr)
end

-- 2. Lua 栈追踪
local function stack_trace()
    ngx.log(ngx.INFO, debug.traceback())
end

-- 3. 共享内存使用情况
local function dump_shared_dict(name)
    local dict = ngx.shared[name]
    if not dict then return end

    ngx.log(ngx.INFO, "Shared dict: ", name)
    ngx.log(ngx.INFO, "  Capacity: ", dict:capacity())
    ngx.log(ngx.INFO, "  Free space: ", dict:free_space())

    local keys = dict:get_keys(100)
    for _, key in ipairs(keys) do
        local value = dict:get(key)
        ngx.log(ngx.INFO, "  ", key, " = ", tostring(value))
    end
end

-- 4. 性能计时
local function timer_start()
    return ngx.now()
end

local function timer_end(start, label)
    local elapsed = (ngx.now() - start) * 1000
    ngx.log(ngx.INFO, label, ": ", elapsed, "ms")
end

16.5.3 紧急回滚

#!/bin/bash
# scripts/rollback.sh

echo "=== Emergency Rollback ==="

# 1. 回滚 Nginx 配置
if [ -f /etc/openresty/nginx.conf.bak ]; then
    cp /etc/openresty/nginx.conf.bak /etc/openresty/nginx.conf
    echo "Nginx config restored"
fi

# 2. 回滚 Lua 代码
if [ -d /usr/local/openresty/lua.bak ]; then
    rm -rf /usr/local/openresty/lua
    cp -r /usr/local/openresty/lua.bak /usr/local/openresty/lua
    echo "Lua code restored"
fi

# 3. 重新加载
openresty -t && openresty -s reload
echo "=== Rollback Complete ==="

16.6 运维手册

16.6.1 日常运维命令

# 查看版本
openresty -V

# 测试配置
openresty -t

# 优雅重启
openresty -s reload

# 查看 Worker 进程
ps aux | grep nginx

# 查看连接数
ss -s

# 查看错误日志
tail -f /var/log/openresty/error.log

# 查看访问日志
tail -f /var/log/openresty/access.log

# 查看共享内存使用
curl http://localhost:8080/admin/shared-memory

16.6.2 监控告警

告警名称 条件 严重程度
高错误率 5xx > 5% 持续 5 分钟 P0
高延迟 P99 > 2s 持续 5 分钟 P1
内存泄漏 内存持续增长 30 分钟 P1
Worker 重启 Worker 进程异常退出 P0
后端不可用 健康检查连续失败 P0
限流触发 429 > 10% 持续 5 分钟 P2
缓存命中率低 < 30% 持续 10 分钟 P2

16.7 总结

恭喜你完成了 OpenResty 高性能网关开发教程的全部 16 章!

回顾

章节 内容
01-04 基础知识:OpenResty 架构、安装、Nginx Lua 生命周期、Lua 语言
05-08 核心功能:路由、限流、认证、代理
09-12 高级特性:缓存、转换、日志、安全
13-16 架构部署:微服务、Docker、测试、最佳实践

下一步

  1. 实践:搭建自己的 OpenResty 网关,从简单功能开始
  2. 阅读源码:学习 Kong、APISIX 的实现
  3. 深入学习:LuaJIT FFI、Nginx C 模块开发
  4. 社区参与:加入 OpenResty 社区,参与讨论和贡献

推荐资源

资源 链接
OpenResty 官方文档 https://openresty.org/en/
OpenResty 最佳实践 https://moonbingbing.gitbooks.io/openresty-best-practices/
lua-resty 系列库 https://github.com/openresty/lua-resty-*
Kong 文档 https://docs.konghq.com/
APISIX 文档 https://apisix.apache.org/
LuaJIT 文档 https://luajit.org/

上一章:← 第 15 章 - 测试与质量保障 回到目录:教程首页 →