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、测试、最佳实践 |
下一步
- 实践:搭建自己的 OpenResty 网关,从简单功能开始
- 阅读源码:学习 Kong、APISIX 的实现
- 深入学习:LuaJIT FFI、Nginx C 模块开发
- 社区参与:加入 OpenResty 社区,参与讨论和贡献
推荐资源
上一章:← 第 15 章 - 测试与质量保障
回到目录:教程首页 →