你好,哈布尔!
今天我们就来看看如何在 Nginx 中使用 Lua:动态路由、流量均衡、头部欺骗和实时请求体转换。OpenResty 和 lua-nginx-module 允许你将一些逻辑移动到 Web 服务器级别,减少延迟并提高灵活性。
通过 lua-resty-balancer 进行动态上游更改
我喜欢 OpenResty 的最酷的功能之一是能够在运行时更改上游。更改配置时无需重启服务器,只需更新lua_shared_dict中的值,一切就会正常。
配置示例:
http {
lua_shared_dict balancer_cache 10m;
upstream dynamic_upstream {
server 127.0.0.1:8080; # Резервный сервер по умолчанию
}
server {
listen 80;
server_name example.com;
location / {
access_by_lua_block {
local balancer_cache = ngx.shared.balancer_cache
local new_upstream = balancer_cache:get("current_upstream")
if new_upstream then
ngx.var.upstream = new_upstream
else
ngx.var.upstream = "dynamic_upstream"
end
ngx.log(ngx.INFO, "Используем upstream: ", ngx.var.upstream)
}
proxy_pass http://$upstream;
}
}
}
我们将当前上游存储在 lua_shared_dict 中,并可以动态更改它,例如,通过 REST API 或 cron 任务。通过这种方式,您甚至可以实现故障转移机制、A/B 测试和一些平衡方案。但是,正如他们所说,能力越大,责任越大:始终添加错误处理和日志记录。
欺骗标头并动态更改请求/响应正文
还可以实时更改请求/响应的标头和正文。
更改标头的示例:
http {
lua_shared_dict balancer_cache 10m;
upstream dynamic_upstream {
server 127.0.0.1:8080; # Резервный сервер по умолчанию
}
server {
listen 80;
server_name example.com;
location / {
access_by_lua_block {
local balancer_cache = ngx.shared.balancer_cache
local new_upstream = balancer_cache:get("current_upstream")
if new_upstream then
ngx.var.upstream = new_upstream
else
ngx.var.upstream = "dynamic_upstream"
end
ngx.log(ngx.INFO, "Используем upstream: ", ngx.var.upstream)
}
proxy_pass http://$upstream;
}
}
}
替换 User-Agent 标头。但可能性并不止于此:您可以根据业务逻辑动态添加、删除或修改任何标头。现在是一个更改响应正文的示例:
server {
listen 80;
server_name example.com;
location /modify_body {
proxy_pass http://backend_service;
header_filter_by_lua_block {
ngx.header["Content-Type"] = "application/json"
}
body_filter_by_lua_block {
local chunk = ngx.arg[1]
if chunk then
-- Пример: добавляем поле "extra": "value" в JSON-ответ.
chunk = string.gsub(chunk, "}", ', "extra": "value"}')
end
ngx.arg[1] = chunk
}
}
}
通过添加新字段来调味 JSON 响应。当然,您还需要更准确地解析 JSON 并检查数据的正确性。但这里最主要的是要理解原理:您可以随时随地更改数据,而不会干扰后端。
Nginx 作为具有动态路由的 API 网关
我们使用动态路由规则来存储它们,这使它们能够适应不断变化的条件,无论是 A/B 测试、故障转移到备用服务器还是平滑的配置更新。lua_shared_dict
http {
lua_shared_dict route_config 1m;
server {
listen 80;
server_name api.example.com;
location / {
access_by_lua_block {
local route_config = ngx.shared.route_config
local uri = ngx.var.uri
local route = route_config:get(uri)
if route then
ngx.var.target = route
else
ngx.var.target = "default_backend"
ngx.log(ngx.WARN, "Маршрут для URI ", uri, " не найден, используем default_backend")
end
ngx.log(ngx.INFO, "Маршрутизируем URI: ", uri, " к ", ngx.var.target)
}
proxy_pass http://$target;
}
}
}
lua_shared_dict route_config
允许您在不重启 Nginx 的情况下存储和修改路由规则,这本质上是通过 API 或按计划更新的基础。我们根据 URI 定义一个路由,如果它不可用,我们会自动使用 ,从而进行一些容错。access_by_lua_block
default_backend
Redis+Lua:流量均衡管理
当 Nginx 需要快速适应不断变化的条件时,将路由逻辑移动到即时访问存储库是有意义的。Redis + Lua 是一个强大的组合,允许您在不重启服务器的情况下动态管理流量。
Redis 与 Nginx 的集成示例:
http {
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
lua_shared_dict redis_cache 10m;
server {
listen 80;
server_name api.example.com;
location /traffic {
access_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- Таймаут 1 секунда
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "Ошибка подключения к Redis: ", err)
return ngx.exit(500)
end
local route, err = red:get("route:" .. ngx.var.uri)
ngx.var.backend = (route and route ~= ngx.null) and route or "default_backend"
ngx.log(ngx.INFO, "Выбран backend: ", ngx.var.backend)
-- Возвращаем соединение в пул
local ok, err = red:set_keepalive(10000, 100)
if not ok then
ngx.log(ngx.ERR, "Ошибка возврата соединения в пул: ", err)
end
}
proxy_pass http://$backend;
}
}
}
Nginx 在每次请求之前都会通过 key 联系 Redis,选择目标后端,这样可以集中管理均衡,快速重定向负载;但是,高性能是通过最小的 Redis 延迟和用于缓存来实现的,并且通过 配置的连接池通过返回未使用的 TCP 连接(最多 100 个连接,超时为 10 秒)来节省资源,而不是为每个请求创建一个新连接。route:/traffic
lua_shared_dict
set_keepalive
如果您有任何问题或想分享您的技巧,请在评论中写下。
作为行业专家提供的实用在线课程的一部分,您可以获得更相关的 IT 基础架构技能。在目录中,您可以看到所有程序的列表,在日历中,您可以注册公开课程。