lua-resty-checkups

在纯 ngx_lua 中管理 Nginx 上游

$ opm get firesnow/lua-resty-checkups

名称

lua-resty-checkups - 在纯 ngx_lua 中管理 Nginx 上游

[!构建状态](https://travis-ci.org/upyun/lua-resty-checkups)

状态

在大多数情况下可能已经可以投入生产,但尚未在实际环境中得到验证。请查看问题列表,如果您有任何问题或疑问,请告诉我。

功能

  • 定期向上游服务器发送心跳

  • 主动和被动健康检查

  • 动态上游更新

  • 通过加权轮询或一致哈希进行负载均衡

  • 与 Nginx 上游块同步

  • 按层级或键尝试集群

概述

        -- config.lua
    
        _M = {}
    
        _M.global = {
            checkup_timer_interval = 15,
            checkup_shd_sync_enable = true,
            shd_config_timer_interval = 1,
        }
    
        _M.ups1 = {
            cluster = {
                {
                    servers = {
                        {host="127.0.0.1", port=4444, weight=10, max_fails=3, fail_timeout=10},
                    }
                },
            },
        }
    
        return _M


        -- nginx.conf
    
        lua_package_path "/path/to/lua-resty-checkups/lib/?.lua;/path/to/config.lua;;";
    
        lua_shared_dict state 10m;
        lua_shared_dict mutex 1m;
        lua_shared_dict locks 1m;
        lua_shared_dict config 10m;
    
        server {
            listen 12350;
            return 200 12350;
        }
    
        server {
            listen 12351;
            return 200 12351;
        }
    
        init_worker_by_lua_block {
            local config = require "config"
            local checkups = require "resty.checkups.api"
    
            checkups.prepare_checker(config)
            checkups.create_checker()
        }
    
        server {
            location = /12350 {
                proxy_pass http://127.0.0.1:12350/;
            }
            location = /12351 {
                proxy_pass http://127.0.0.1:12351/;
            }
    
            location = /t {
                content_by_lua_block {
                    local checkups = require "resty.checkups.api"
    
                    local callback = function(host, port)
                    local res = ngx.location.capture("/" .. port)
                        ngx.say(res.body)
                        return 1
                    end
    
                    local ok, err
    
                    -- connect to a dead server, no upstream available
                    ok, err = checkups.ready_ok("ups1", callback)
                    if err then ngx.say(err) end
    
                    -- add server to ups1
                    ok, err = checkups.update_upstream("ups1", {
                        {
                            servers = {
                                {host="127.0.0.1", port=12350, weight=10, max_fails=3, fail_timeout=10},
                            }
                        },
                    })
    
                    if err then ngx.say(err) end
                    ngx.sleep(1)
                    ok, err = checkups.ready_ok("ups1", callback)
                    if err then ngx.say(err) end
                    ok, err = checkups.ready_ok("ups1", callback)
                    if err then ngx.say(err) end
    
                    -- add server to new upstream
                    ok, err = checkups.update_upstream("ups2", {
                            {
                                servers = {
                                    {host="127.0.0.1", port=12351},
                                }
                            },
                        })
                    if err then ngx.say(err) end
                    ngx.sleep(1)
                    ok, err = checkups.ready_ok("ups2", callback)
                    if err then ngx.say(err) end
    
                    -- add server to ups2, reset rr state
                    ok, err = checkups.update_upstream("ups2", {
                            {
                                servers = {
                                    {host="127.0.0.1", port=12350, weight=10, max_fails=3, fail_timeout=10},
                                    {host="127.0.0.1", port=12351, weight=10, max_fails=3, fail_timeout=10},
                                }
                            },
                        })
                    if err then ngx.say(err) end
                    ngx.sleep(1)
                    ok, err = checkups.ready_ok("ups2", callback)
                    if err then ngx.say(err) end
                    ok, err = checkups.ready_ok("ups2", callback)
                    if err then ngx.say(err) end
                }
            }
        }

上面定义的/t 位置的典型输出为

    no servers available
    12350
    12350
    12351
    12350
    12351

配置

Lua 配置

Checkups 的配置文件是一个 lua 模块,它包含两个部分:全局部分和集群部分。

下面展示了一个 checkups 的示例配置文件:

        -- config.lua
    
        -- Here is the global part
    
        _M = {}
    
        _M.global = {
            checkup_timer_interval = 15,
            checkup_timer_overtime = 60,
            default_heartbeat_enable = true,
            checkup_shd_sync_enable = true,
            shd_config_timer_interval = 1,
        }
    
    
        -- The rests parts are cluster configurations
    
        _M.redis = {
            enable = true,
            typ = "redis",
            timeout = 2,
            read_timeout = 15,
            send_timeout = 15,
    
            protected = true,
    
            cluster = {
                {   -- level 1
                        try = 2,
                    servers = {
                        { host = "192.168.0.1", port = 6379, weight=10, max_fails=3, fail_timeout=10 },
                        { host = "192.168.0.2", port = 6379, weight=10, max_fails=3, fail_timeout=10 },
                    }
                },
                {   -- level 2
                    servers = {
                        { host = "192.168.0.3", port = 6379, weight=10, max_fails=3, fail_timeout=10 },
                    }
                },
            },
        }
    
        _M.api = {
            enable = false,
            typ = "http",
                http_opts = {
                query = "GET /status HTTP/1.1\r\nHost: localhost\r\n\r\n",
                statuses = {
                        [500] = false,
                        [502] = false,
                        [503] = false,
                        [504] = false,
                },
            },
    
            mode = "hash",
    
            cluster = {
                dc1 = {
                    servers = {
                        { host = "192.168.1.1", port = 1234, weight=10, max_fails=3, fail_timeout=10 },
                    }
                },
                dc2 = {
                    servers = {
                        { host = "192.168.1.2", port = 1234, weight=10, max_fails=3, fail_timeout=10 },
                    }
                }
            }
        }
    
        _M.ups_from_nginx = {
            timeout = 2,
    
            cluster = {
                {   -- level 1
                    upstream = "api.com",
                },
                {   -- level 2
                    upstream = "api.com",
                    upstream_only_backup = true,
                },
            },
        }
    
        return _M

全局配置

  • checkup_timer_interval: 向后端服务器发送心跳的间隔。默认值为5

  • checkup_timer_overtime: 检查以使计时器键过期的间隔。在大多数情况下,您不需要更改此值。默认值为60

  • default_heartbeat_enable: Checkups 默认情况下是否会向服务器发送心跳。默认值为true

  • checkup_shd_sync_enable: 为每个工作进程创建上游同步器。如果设置为false,动态上游将无法正常工作。默认值为true

  • shd_config_timer_interval: 从共享内存同步上游列表的间隔。默认值等于checkup_timer_interval

  • ups_status_sync_enable: 如果设置为true,checkups 将从 checkups 同步上游状态到 Nginx 上游块。默认值为false

  • ups_status_timer_interval: 从 checkups 同步上游状态到 Nginx 上游块的间隔。

集群配置

  • skey: _M.xxxxxxxxxx是此集群的skey(服务键)。

  • enable: 启用或禁用向服务器发送心跳。默认值为true

  • typ: 集群类型,必须是generalredismysqlhttp之一。默认值为general

    • general: 通过 TCP sock:connect 发送心跳。

    • redis: 通过 redis PING 发送心跳。需要lua-resty-redis模块。

    • mysql: 通过 mysql db:connect 发送心跳。需要lua-resty-mysql模块。

    • http: 通过 HTTP 请求发送心跳。您可以在http_opts中设置自定义的 HTTP 请求和响应代码。

  • timeout: 连接到上游服务器的超时时间。默认值为5

  • read_timeout: 从上游服务器读取数据的超时时间。默认值等于timeout

  • send_timeout: 向上游服务器写入数据的超时时间。默认值等于timeout

  • http_opts: HTTP 心跳配置。仅适用于typ="http"

    • query: 用于发送心跳的 HTTP 请求。

    • statuses: 如果服务器返回的代码设置为false,则认为服务器已失败。

  • mode: 负载均衡模式。可以设置为hashurl_haship_hash。Checkups 将根据hash_keyngx.var.uringx.var.remote_addr对服务器进行负载均衡。默认值为wrr

  • protected: 如果设置为true,并且集群中的所有服务器都已失败,checkups 不会将最后一个失败的服务器标记为不可用(err),而是将其标记为unstable(在下一次尝试中仍然可用)。默认值为true

  • cluster: 您可以根据集群优先级配置多个层级,在每个层级上,您可以配置一个servers集群。只有当先前层级中的所有服务器都被认为不可用时,checkups 才会尝试下一层级。

除了按层级尝试集群之外,您还可以配置 checkups 按键尝试集群(请参阅上面的api集群)。请记住,您还应传递额外的参数(例如opts.cluster_key={"dc1", "dc2"}opts.cluster_key={3, 1, 2})到checkups.read_ok,以使 checkups 按dc1dc2level 3level 1level 2的顺序进行尝试。如果您没有向checkups.ready_ok传递opts.cluster_key,checkups 仍然会按层级尝试集群。对于上面的api集群,checkups 最终会返回no servers available

  • try: 重试次数。默认值为服务器数量。

  • try_timeout: 限制请求响应的时间,类似于 nginx proxy_next_upstream_timeout

  • servers: servers的配置列出如下:

    • weight: 设置服务器的权重。默认值为1

    • max_fails: 设置在fail_timeout参数设置的持续时间内,与服务器通信失败的尝试次数。默认情况下,失败尝试次数设置为0,这将禁用尝试计数。如果typ="http",则通过http_opts.statuses定义什么被视为失败尝试,或者通过checkups.ready_ok返回nil/false。此选项仅在轮询中可用。

    • fail_timeout: 设置在持续时间内,应发生与服务器通信的指定次数失败尝试以将服务器视为不可用,以及服务器被视为不可用的时间段。默认情况下,该参数设置为10秒。此选项仅在轮询中可用。

  • upstream: Nginx 上游块的名称。Checkups 将在prepare_checker中从 Nginx conf 的上游块中提取服务器。需要lua-upstream-nginx-module模块。

  • upstream_only_backup: 如果设置为true,checkups 将仅从 Nginx 上游块中提取备份服务器。

Nginx 配置

将 lua 配置文件和 checkups 的路径添加到lua_package_path中,并创建 checkups 使用的 lua 共享字典。您应该将这些行放入 Nginx 配置文件中的http块中。

    lua_package_path "/path/to/lua-resty-checkups/lib/?.lua;/path/to/config.lua;;";

    lua_shared_dict state 10m;
    lua_shared_dict mutex 1m;
    lua_shared_dict locks 1m;
    lua_shared_dict config 10m;

API

!

prepare_checker

语法: prepare_checker(config)

阶段: init_by_lua init_worker_by_lua

将配置从config.lua复制到 checkups,从 Nginx 上游块中提取服务器,并进行一些基本初始化。此方法可以在init阶段或init_worker阶段调用。如果您想要从 Nginx 上游块中提取服务器,则必须在init_worker阶段调用此方法。

create_checker

语法: create_checker()

阶段: init_worker_by_lua

创建心跳计时器和上游同步计时器。在所有工作进程中,只创建一个心跳计时器。强烈建议在init_worker阶段调用此方法。

ready_ok

语法: res, err = ready_ok(skey, callback, opts?)

阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

从集群skey中选择一个可用的peer,并调用callback(peer.host, peer.port, opts)

opts表接受以下字段:

  • cluster_key: 按cluster_key尝试集群。Checkups 将按cluster_key的顺序尝试集群。clusters_key可以是集群的名称或集群的层级。集群示例:{"cluster_name_A", "name_B", "name_C"}。层级示例:{3, 2, 1}

  • hash_key: 在hash负载均衡模式中使用的键。如果未设置,将使用ngx.req.uri

  • try: 重试次数不超过try次。

  • try_timeout: 限制请求响应的时间,类似于 nginx proxy_next_upstream_timeout

如果成功,返回callback的返回值,否则返回nil和一个描述错误的字符串。

如果callback返回nilfalse,checkups 将认为这是一个失败的尝试,并将使用另一个 peer 重试callback。因此,**始终记住不要在成功的回调后返回nilfalse。**

select_peer

语法: peer, err = select_peer(skey)

上下文: rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua

从集群skey中选择一个可用的 peer。

返回一个包含可用 peer 的hostport的表。

如果出现错误,将返回 nil 和一个描述错误的字符串。

get_status

语法: status = get_status()

阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

json格式返回 checkups 状态。

get_ups_timeout

语法: connect_timeout, send_timeout, read_timeout = get_ups_timeout(skey)

阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

返回集群skey的超时时间。

feedback_status

语法: ok, err = feedback_status(skey, host, port, failed)

上下文: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, balancer_by_lua.*

将集群skey中的服务器host:port标记为失败(true)或可用(false)。

如果成功,返回1,否则返回nil和一个描述错误的字符串。

update_upstream

语法: ok, err = update_upstream(skey, upstream)

阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

更新集群skeyupstreamconfig.lua中的cluster格式相同。

如果成功,返回true,否则返回false和一个描述错误的字符串。

delete_upstream

语法: ok, err = delete_upstream(skey)

阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

从上游列表中删除集群skey

如果成功,返回true,否则返回false和一个描述错误的字符串。

版权和许可

该捆绑包本身是在 2-clause BSD 许可证下授权的。

版权所有 (c) 2016, UPYUN(又拍云) Inc.

此模块是在 BSD 许可证条款下授权的。

允许以源代码和二进制形式重新分发和使用,无论是否修改,前提是满足以下条件

  • 重新分发的源代码必须保留上述版权声明、此条件列表以及以下免责声明。

  • 二进制形式的重新分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、此条件列表以及以下免责声明。

本软件由版权所有者和贡献者“按原样”提供,并且任何明示或暗示的担保,包括但不限于适销性和特定用途适用性的暗示担保,均被免除。在任何情况下,版权所有者或贡献者均不对任何直接的、间接的、偶然的、特殊的、示例性的或后果性的损害(包括但不限于替代商品或服务的采购;使用、数据或利润损失;或业务中断)负责,无论其因本软件的使用而产生,无论其是根据合同、严格责任或侵权(包括疏忽或其他原因)产生的,即使已被告知此类损害的可能性。

另请参阅

作者

firesnow

许可证

2bsd

版本