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

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 章 - 测试与质量保障 回到目录教程首页 →