Nginx 中的 Lua:动态请求路由

你好,哈布尔!

今天我们就来看看如何在 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_blockdefault_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:/trafficlua_shared_dictset_keepalive


如果您有任何问题或想分享您的技巧,请在评论中写下。

作为行业专家提供的实用在线课程的一部分,您可以获得更相关的 IT 基础架构技能。在目录中,您可以看到所有程序的列表,在日历中,您可以注册公开课程。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇