7.9.5 动态路由与API网关实践
动态路由与API网关实践重点在于以OpenResty作为统一流量入口,通过Lua实现路由、鉴权、灰度、限流与观测。典型架构如下:
环境准备与安装示例#
以下以OpenResty社区版为例,适合在测试环境快速验证动态路由与网关能力。
# 1) 安装依赖(以CentOS 7为例)
yum -y install gcc pcre-devel openssl-devel zlib-devel git
# 2) 下载并安装OpenResty
cd /usr/local/src
wget https://openresty.org/download/openresty-1.21.4.2.tar.gz
tar -zxvf openresty-1.21.4.2.tar.gz
cd openresty-1.21.4.2
./configure --prefix=/usr/local/openresty
make -j4 && make install
# 3) 验证
/usr/local/openresty/nginx/sbin/nginx -v
关键命令解释
- ./configure --prefix:指定安装目录,便于统一管理。
- make -j4:并行编译,提升速度。
- nginx -v:验证版本与编译成功。
目录与配置文件建议#
# 推荐目录结构
/usr/local/openresty/nginx/
├── conf/
│ ├── nginx.conf
│ ├── conf.d/
│ │ └── gateway.conf
├── lua/
│ ├── router.lua
│ ├── auth.lua
│ └── limiter.lua
└── logs/
动态路由核心配置示例#
nginx.conf(关键片段)
worker_processes auto;
events { worker_connections 10240; }
http {
lua_shared_dict routes 10m;
lua_shared_dict limits 10m;
lua_shared_dict tokens 10m;
init_worker_by_lua_file /usr/local/openresty/nginx/lua/router.lua;
server {
listen 80;
server_name localhost;
location /api/ {
access_by_lua_file /usr/local/openresty/nginx/lua/auth.lua;
access_by_lua_file /usr/local/openresty/nginx/lua/limiter.lua;
# 动态选择后端
set $upstream_host '';
access_by_lua_block {
local route = ngx.shared.routes:get(ngx.var.request_uri)
if not route then
return ngx.exit(404)
end
ngx.var.upstream_host = route
}
proxy_pass http://$upstream_host;
}
}
}
router.lua(服务发现与路由加载)
-- /usr/local/openresty/nginx/lua/router.lua
local cjson = require "cjson.safe"
local http = require "resty.http"
local routes = ngx.shared.routes
local function refresh_routes()
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=svcA", {
method = "GET",
timeout = 2000
})
if not res then
ngx.log(ngx.ERR, "nacos fetch failed: ", err)
return
end
-- 示例:将 /api/v1/* 路由到 svcA 实例
local data = cjson.decode(res.body) or {}
local instance = data.hosts and data.hosts[1]
if instance then
local host = instance.ip .. ":" .. instance.port
routes:set("/api/v1/", host, 10) -- 10秒过期
end
end
local ok, err = ngx.timer.every(5, refresh_routes)
if not ok then
ngx.log(ngx.ERR, "timer failed: ", err)
end
auth.lua(鉴权示例:API Key)
-- /usr/local/openresty/nginx/lua/auth.lua
local token = ngx.req.get_headers()["X-API-Key"]
if not token then
ngx.status = 401
ngx.say('{"code":401,"msg":"missing api key"}')
return ngx.exit(401)
end
-- 简化:演示用静态校验
if token ~= "demo-key" then
ngx.status = 403
ngx.say('{"code":403,"msg":"invalid api key"}')
return ngx.exit(403)
end
limiter.lua(令牌桶限流示例)
-- /usr/local/openresty/nginx/lua/limiter.lua
local limit = ngx.shared.limits
local key = ngx.var.remote_addr
local current = limit:get(key) or 0
if current >= 10 then
ngx.status = 429
ngx.say('{"code":429,"msg":"rate limit"}')
return ngx.exit(429)
end
limit:incr(key, 1, 0, 1) -- 初始值1,过期1秒
预期效果
- /api/v1/ 自动路由到 Nacos 中的 svcA 实例。
- X-API-Key 不是 demo-key 直接拒绝。
- 单IP每秒超过10次请求返回 429。
运行与验证#
# 启动/重载
/usr/local/openresty/nginx/sbin/nginx
/usr/local/openresty/nginx/sbin/nginx -s reload
# 验证路由
curl -H "X-API-Key: demo-key" http://127.0.0.1/api/v1/health
# 触发限流
for i in {1..20}; do curl -s -o /dev/null -w "%{http_code}\n" \
-H "X-API-Key: demo-key" http://127.0.0.1/api/v1/health; done
故障排查清单(含命令)#
- 路由不生效
# 检查共享字典是否写入
/usr/local/openresty/nginx/sbin/nginx -T | grep lua_shared_dict
# 查看错误日志
tail -f /usr/local/openresty/nginx/logs/error.log
- 鉴权失败
# 检查请求头
curl -I -H "X-API-Key: demo-key" http://127.0.0.1/api/v1/health
- Nacos不可达
# 测试注册中心接口
curl "http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=svcA"
- 限流过严
# 临时扩大限流阈值
# 修改 limiter.lua 中阈值,reload 生效
路由策略与实践建议(含示例)#
- 路径版本路由
# /api/v1 -> svcA, /api/v2 -> svcB
- Header 灰度
local env = ngx.req.get_headers()["X-Env"]
if env == "canary" then ngx.var.upstream_host = "10.0.0.10:8080" end
- 用户哈希分流
local uid = ngx.req.get_uri_args()["uid"] or "0"
local slot = (ngx.crc32_short(uid) % 100)
if slot < 10 then ngx.var.upstream_host = "10.0.0.10:8080" end
练习题#
- 将
auth.lua改为支持 JWT(仅校验签名与过期时间)。 - 将路由表由“单实例”扩展为“多实例加权轮询”。
- 增加
X-Request-ID并在日志中输出,便于链路追踪。 - 将限流由“IP级”改为“用户级+接口级”。
以上实践可构建具备动态路由、鉴权、限流、灰度与可观测能力的轻量级API网关,满足生产环境的稳定性与扩展性需求。