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

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