跳转至

upstream: NGINX模块lua的上游连接负载均衡和故障转移模块

安装

如果您尚未设置RPM仓库订阅,请注册。然后您可以继续以下步骤。

CentOS/RHEL 7 或 Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-upstream

CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-upstream

要在NGINX中使用此Lua库,请确保已安装nginx-module-lua

本文档描述了lua-resty-upstream v0.10,于2019年12月19日发布。


上游连接负载均衡和故障转移模块

概述

创建一个lua 共享字典。在init_by_lua中定义您的上游池和主机,这将被保存到共享字典中。

使用connect方法返回一个已连接的tcp socket

或者传入一个实现了connect()set_timeout()的resty模块(例如lua-resty-redislua-resty-http)。

调用process_failed_hosts来处理失败的主机,而不会阻塞当前请求。

使用resty.upstream.api在初始化或运行时修改上游配置,推荐这样做!

resty.upstream.http封装了来自@pintsized的lua-resty-http

它允许根据HTTP状态代码以及socket连接状态进行故障转移。

lua_shared_dict my_upstream_dict 1m;
init_by_lua '
    upstream_socket  = require("resty.upstream.socket")
    upstream_api = require("resty.upstream.api")

    upstream, configured = upstream_socket:new("my_upstream_dict")
    if not upstream then
        error(configured)
    end
    api = upstream_api:new(upstream)

    if not configured then -- 仅在启动时重新配置,共享内存在HUP之间持续存在
        api:create_pool({id = "primary", timeout = 100})
        api:set_priority("primary", 0)
        api:set_method("primary", "round_robin")
        api:add_host("primary", { id="a", host = "127.0.0.1", port = "80", weight = 10 })
        api:add_host("primary", { id="b", host = "127.0.0.1", port = "81",  weight = 10 })

        api:create_pool({id = "dr"})
        api:set_priority("dr", 10)
        api:add_host("dr", { host = "127.0.0.1", port = "82", weight = 5 })
        api:add_host("dr", { host = "127.0.0.1", port = "83", weight = 10 })

        api:create_pool({id = "test", priority = 5})
        api:add_host("primary", { id="c", host = "127.0.0.1", port = "82", weight = 10 })
        api:add_host("primary", { id="d", host = "127.0.0.1", port = "83", weight = 10 })
    end
';

init_worker_by_lua 'upstream:init_background_thread()';

server {

    location / {
        content_by_lua '
            local sock, err = upstream:connect()
            upstream:process_failed_hosts()
        ';
    }

}

upstream.socket

new

syntax: upstream, configured = upstream_socket:new(dictionary, id?)

使用提供的字典名称返回一个新的上游对象。当在init_by_lua中调用时,如果字典已经包含配置,则返回一个额外的变量。接受一个可选的id参数,如果多个upstream.socket实例使用相同的字典,则此参数必须唯一。

init_background_thread

syntax: ok, err = upstream:init_background_thread()

初始化后台线程,应在init_worker_by_lua中调用。

connect

syntax: ok, err = upstream:connect(client?, key?)

尝试按优先顺序使用所选的负载均衡方法连接到定义池中的主机。返回一个已连接的socket和一个包含连接的hostpoolidpool的表,或者返回nil和错误消息。

当传入一个socket或resty模块时,它将在成功连接后返回相同的对象或nil。

此外,哈希方法可以接受一个可选的key来定义如何对连接进行哈希以确定主机。默认使用ngx.var.remote_addr。当池的方法是轮询时,此值将被忽略。

resty_redis = require('resty.redis')
local redis = resty_redis.new()

local key = ngx.req.get_headers()["X-Forwarded-For"]

local redis, err = upstream:connect(redis, key)

if not redis then
    ngx.log(ngx.ERR, err)
    ngx.status = 500
    return ngx.exit(ngx.status)
end

ngx.log(ngx.info, 'Connected to ' .. err.host.host .. ':' .. err.host.port)
local ok, err = redis:get('key')

process_failed_hosts

syntax: ok, err = upstream:process_failed_hosts()

处理当前请求中的任何失败或恢复的主机。通过ngx.timer.at立即生成回调,不会阻塞当前请求。

get_pools

syntax: pools = usptream:get_pools()

返回一个包含当前池和主机配置的表。例如:

{
    primary = {
        up = true,
        method = 'round_robin',
        timeout = 100,
        priority = 0,
        hosts = {
            web01 = {
                host = "127.0.0.1",
                weight = 10,
                port = "80",
                lastfail = 0,
                failcount = 0,
                up = true,
                healthcheck = true
            }
            web02 = {
                host = "127.0.0.1",
                weight = 10,
                port = "80",
                lastfail = 0,
                failcount = 0,
                up = true,
                healthcheck = { interval = 30, path = '/check' }
            }
        }
    },
    secondary = {
        up = true,
        method = 'round_robin',
        timeout = 2000,
        priority = 10,
        hosts = {
            dr01 = {
                host = "10.10.10.1",
                weight = 10,
                port = "80",
                lastfail = 0,
                failcount = 0,
                up = true
            }
        }
    },
}

save_pools

syntax: ok, err = upstream:save_pools(pools)

将池的表保存到共享字典中,pools必须与get_pools返回的格式相同。

sort_pools

syntax: ok, err = upstream:sort_pools(pools)

根据提供的池表在共享字典中生成优先级顺序。

bind

syntax: ok, err = upstream:bind(event, func)

绑定一个函数,当事件发生时调用。func应期望一个包含事件数据的参数。

在成功绑定时返回true,失败时返回nil和错误消息。

local function host_down_handler(event)
    ngx.log(ngx.ERR, "Host: ", event.host.host, ":", event.host.port, " in pool '", event.pool.id,'" is down!')
end
local ok, err = upstream:bind('host_down', host_down_handler)

事件:host_up

当主机状态从下线变为上线时触发。事件数据是一个包含受影响主机和池的表。

事件:host_down

当主机状态从上线变为下线时触发。事件数据是一个包含受影响主机和池的表。

upstream.api

这些函数允许您动态重新配置上游池和主机。

new

syntax: api, err = upstream_api:new(upstream)

使用提供的上游对象返回一个新的api对象。

set_method

syntax: ok, err = api:set_method(poolid, method)

为指定的池设置负载均衡方法。目前支持随机轮询和哈希方法。

create_pool

syntax: ok, err = api:create_pool(pool)

从选项表创建一个新池,pool必须至少包含一个键id,该键在当前上游对象中必须唯一。

其他有效选项包括:

  • method 负载均衡方法
  • timeout 连接超时(毫秒)
  • priority 优先级更高的池稍后使用
  • read_timeout
  • keepalive_timeout
  • keepalive_pool
  • status_codes 参见status_codes

此时无法定义主机。

注意:IDs由此函数转换为字符串。

默认池值:

{ method = 'round_robin', timeout = 2000, priority = 0 }

set_priority

syntax: ok, err = api:set_priority(poolid, priority)

优先级必须是数字,出错时返回nil。

add_host

syntax: ok, err = api:add_host(poolid, host)

接受一个池ID和一个选项表,host必须至少包含host。如果未指定主机ID,则将根据池中主机的数量使用数字索引。

注意:IDs由此函数转换为字符串。

默认值:

{ host = '', port = 80, weight = 0}

remove_host

syntax: ok, err = api:remove_host(poolid, host)

接受一个池id和一个要从池中移除的主机id。

down_host

syntax: ok,err = api:down_host(poolid, host)

手动将主机标记为下线,该主机将不会自动恢复。

up_host

syntax: ok,err = api:up_host(poolid, host)

手动将一台死掉的主机恢复到池中。

upstream.http

用于向上游主机发起HTTP请求的函数。

new

syntax: httpc, err = upstream_http:new(upstream, ssl_opts?)

使用提供的上游对象返回一个新的HTTP上游对象。

ssl_opts是一个可选表,用于配置SSL支持。 * ssl 设置为true以启用SSL握手,默认值为false * ssl_verify 设置为false以禁用SSL证书验证,默认值为true * sni_host 用作SNI主机名的字符串,默认是请求的Host头

lua https_upstream = Upstream_HTTP:new(upstream_ssl, { ssl = true, ssl_verify = true, sni_host = "foo.example.com" })

init_background_thread

syntax: ok, err = upstream_http:init_background_thread()

初始化后台线程,应在init_worker_by_lua中调用。

如果使用upstream.http后台线程,请不要upstream.socket中调用init_background_thread方法。

request

syntax: res, err_or_conn_info, status? = upstream_api:request(params)

接受与lua-resty-http的request方法相同的参数。

在成功请求时返回lua-resty-http对象和一个包含连接主机和池的表。

如果请求失败,则返回nil、错误和建议的HTTP状态码。

local ok, err, status = upstream_http:request({
        path = "/helloworld",
        headers = {
            ["Host"] = "example.com",
        }
    })
if not ok then
    ngx.status = status
    ngx.say(err)
    ngx.exit(status)
else
    local host = err.host
    local pool = err.pool
end

set_keepalive

syntax: ok, err = upstream_http:set_keepalive()

将保持活动超时/池从池配置传递给lua-resty-http的set_keepalive方法。

get_reused_times

syntax: ok, err = upstream_http:get_reused_times()

传递给lua-resty-http的get_reused_times方法。

close

syntax: ok, err = upstream_http:close()

传递给lua-resty-http的close方法。

HTTP健康检查

通过将healthcheck参数添加到主机,可以启用主动的后台健康检查。

值为true将启用默认检查,即对/GET请求。healthcheck参数也可以是一个有效的lua-resty-http的request方法的参数表。

附加几个参数:

  • interval 设置健康检查之间的时间,以秒为单位。必须大于或等于10秒。默认值为60秒。
  • timeout 设置健康检查的连接超时。默认为池设置。
  • read_timeout 设置健康检查的读取超时。默认为池设置。
  • status_codes 一个无效响应状态码的表。默认为池设置。

后台检查的失败依据与前端请求相同,除非明确覆盖。

-- 自定义检查参数
api:add_host("primary", {
     host = 123.123.123.123,
     port = 80,
     healthcheck = {
        interval = 30, -- 每30秒检查一次
        timeout      = (5*1000), -- 5秒连接超时
        read_timeout = (15*1000), -- 15秒读取超时
        status_codes = {["5xx"] = true, ["403"] = true}, -- 5xx和403响应为失败
        -- resty-http参数
        path = "/check",
        headers = {
            ["Host"] = "domain.com",
            ["Accept-Encoding"] = "gzip"
        }
     }
})

-- 默认检查参数
api:add_host("primary", {host = 123.123.123.123, port = 80, healthcheck = true})

GitHub

您可以在nginx-module-upstream的GitHub仓库中找到此模块的其他配置提示和文档。