{"id":13685106,"url":"https://github.com/upyun/lua-resty-checkups","last_synced_at":"2026-04-01T18:31:40.473Z","repository":{"id":82554883,"uuid":"65955968","full_name":"upyun/lua-resty-checkups","owner":"upyun","description":"Manage Nginx upstreams in pure Lua.","archived":false,"fork":false,"pushed_at":"2019-11-26T07:53:52.000Z","size":492,"stargazers_count":258,"open_issues_count":10,"forks_count":64,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-03-26T17:35:53.257Z","etag":null,"topics":["lua-resty"],"latest_commit_sha":null,"homepage":null,"language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/upyun.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-08-18T01:50:59.000Z","updated_at":"2023-08-04T11:00:04.000Z","dependencies_parsed_at":"2023-03-12T16:11:02.603Z","dependency_job_id":null,"html_url":"https://github.com/upyun/lua-resty-checkups","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upyun%2Flua-resty-checkups","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upyun%2Flua-resty-checkups/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upyun%2Flua-resty-checkups/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upyun%2Flua-resty-checkups/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/upyun","download_url":"https://codeload.github.com/upyun/lua-resty-checkups/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224230729,"owners_count":17277373,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua-resty"],"created_at":"2024-08-02T14:00:43.936Z","updated_at":"2026-04-01T18:31:40.437Z","avatar_url":"https://github.com/upyun.png","language":"Lua","funding_links":[],"categories":["Lua","Libraries"],"sub_categories":[],"readme":"Name\n====\n\nlua-resty-checkups - Manage Nginx upstreams in pure ngx_lua\n\n[![Build Status](https://travis-ci.org/upyun/lua-resty-checkups.svg)](https://travis-ci.org/upyun/lua-resty-checkups)\n\nTable of Contents\n=================\n\n* [Name](#name)\n* [Status](#status)\n* [Features](#features)\n* [Installation](#installation)\n* [Compatibility](#compatibility)\n* [Synopsis](#synopsis)\n* [Configuration](#configuration)\n    * [Lua configuration](#lua-configuration)\n    * [global configuration](#global-configuration)\n    * [cluster configuration](#cluster-configuration)\n    * [Nginx configuration](#nginx-configuration)\n* [API](#api)\n    * [init](#init)\n    * [prepare_checker](#prepare_checker)\n    * [create_checker](#create_checker)\n    * [ready_ok](#ready_ok)\n    * [select_peer](#select_peer)\n    * [get_status](#get_status)\n    * [get_ups_timeout](#get_ups_timeout)\n    * [feedback_status](#feedback_status)\n    * [update_upstream](#update_upstream)\n    * [delete_upstream](#delete_upstream)\n* [Copyright and License](#copyright-and-license)\n\nStatus\n======\n\nProbably production ready in most cases, though not yet proven in the wild. Please check the issues list and let me know if you have any problems / questions.\n\nFeatures\n========\n\n* Periodically heartbeat to upstream servers\n* Proactive and passive health check\n* Dynamic upstream update\n* Balance by weighted round-robin or consistent-hash\n* Synchronize with Nginx upstream blocks\n* Try clusters by levels or by keys\n\nInstallation\n============\n\n* [LuaRocks](https://luarocks.org/):\n\n```bash\n$ luarocks install lua-resty-checkups\n```\n\n* [OPM](https://github.com/openresty/opm):\n\n```bash\n$ opm get upyun/lua-resty-checkups\n```\n\n* Manually:\n\nJust tweeks the `lua_package_path` or the `LUA_PATH` environment variable, to add the installation path for this Lua module:\n\n```\n/path/to/lua-resty-checkups/lib/resty/?.lua;\n```\n\n\nCompatibility\n=============\n\n* [ngx_http_lua_module](https://github.com/openresty/lua-nginx-module): v0.9.20 or higher.\n\n\nSynopsis\n========\n\n```lua\n    -- config.lua\n\n    _M = {}\n\n    _M.global = {\n        checkup_timer_interval = 15,\n        checkup_shd_sync_enable = true,\n        shd_config_timer_interval = 1,\n    }\n\n    _M.ups1 = {\n        cluster = {\n            {\n                servers = {\n                    { host = \"127.0.0.1\", port = 4444, weight=10, max_fails=3, fail_timeout=10 },\n                }\n            },\n        },\n    }\n\n    return _M\n```\n\n```lua\n    -- nginx.conf\n\n    lua_package_path \"/path/to/lua-resty-checkups/lib/?.lua;/path/to/config.lua;;\";\n\n    lua_shared_dict state 10m;\n    lua_shared_dict mutex 1m;\n    lua_shared_dict locks 1m;\n    lua_shared_dict config 10m;\n\n    server {\n        listen 12350;\n        return 200 12350;\n    }\n\n    server {\n        listen 12351;\n        return 200 12351;\n    }\n\n    init_by_lua_block {\n        local config = require \"config\"\n        local checkups = require \"resty.checkups.api\"\n        checkups.init(config)\n    }\n\n    init_worker_by_lua_block {\n        local config = require \"config\"\n        local checkups = require \"resty.checkups.api\"\n\n        checkups.prepare_checker(config)\n        checkups.create_checker()\n    }\n\n    server {\n        location = /12350 {\n            proxy_pass http://127.0.0.1:12350/;\n        }\n        location = /12351 {\n            proxy_pass http://127.0.0.1:12351/;\n        }\n\n        location = /t {\n            content_by_lua_block {\n                local checkups = require \"resty.checkups.api\"\n\n                local callback = function(host, port)\n                    local res = ngx.location.capture(\"/\" .. port)\n                    ngx.say(res.body)\n                    return 1\n                end\n\n                local ok, err\n\n                -- connect to a dead server, no upstream available\n                ok, err = checkups.ready_ok(\"ups1\", callback)\n                if err then ngx.say(err) end\n\n                -- add server to ups1\n                ok, err = checkups.update_upstream(\"ups1\", {\n                    {\n                        servers = {\n                            { host = \"127.0.0.1\", port = 12350, weight=10, max_fails=3, fail_timeout=10 },\n                        }\n                    },\n                })\n\n                if err then ngx.say(err) end\n                ngx.sleep(1)\n                ok, err = checkups.ready_ok(\"ups1\", callback)\n                if err then ngx.say(err) end\n                ok, err = checkups.ready_ok(\"ups1\", callback)\n                if err then ngx.say(err) end\n\n                -- add server to new upstream\n                ok, err = checkups.update_upstream(\"ups2\", {\n                        {\n                            servers = {\n                                { host=\"127.0.0.1\", port=12351 },\n                            }\n                        },\n                    })\n                if err then ngx.say(err) end\n                ngx.sleep(1)\n                ok, err = checkups.ready_ok(\"ups2\", callback)\n                if err then ngx.say(err) end\n\n                -- add server to ups2, reset rr state\n                ok, err = checkups.update_upstream(\"ups2\", {\n                        {\n                            servers = {\n                                { host = \"127.0.0.1\", port = 12350, weight=10, max_fails=3, fail_timeout=10 },\n                                { host = \"127.0.0.1\", port = 12351, weight=10, max_fails=3, fail_timeout=10 },\n                            }\n                        },\n                    })\n                if err then ngx.say(err) end\n                ngx.sleep(1)\n                ok, err = checkups.ready_ok(\"ups2\", callback)\n                if err then ngx.say(err) end\n                ok, err = checkups.ready_ok(\"ups2\", callback)\n                if err then ngx.say(err) end\n            }\n        }\n    }\n```\n\nA typical output of the `/t` location defined above is:\n\n    no servers available\n    12350\n    12350\n    12351\n    12350\n    12351\n\nConfiguration\n=============\n\nLua configuration\n-----------------\n\nConfiguration file of checkups is a lua module consists of two parts, the global part and the cluster part.\n\n\nAn example configuration file of checkups is shown below,\n\n\n```lua\n    -- config.lua\n\n    -- Here is the global part\n\n    _M = {}\n\n    _M.global = {\n        checkup_timer_interval = 15,\n        checkup_timer_overtime = 60,\n        default_heartbeat_enable = true,\n        checkup_shd_sync_enable = true,\n        shd_config_timer_interval = 1,\n    }\n\n\n    -- The rests parts are cluster configurations\n\n    _M.redis = {\n        enable = true,\n        typ = \"redis\",\n        timeout = 2,\n        read_timeout = 15,\n        send_timeout = 15,\n\n        protected = true,\n\n        cluster = {\n            {   -- level 1\n                    try = 2,\n                servers = {\n                    { host = \"192.168.0.1\", port = 6379, weight=10, max_fails=3, fail_timeout=10 },\n                    { host = \"192.168.0.2\", port = 6379, weight=10, max_fails=3, fail_timeout=10 },\n                }\n            },\n            {   -- level 2\n                servers = {\n                    { host = \"192.168.0.3\", port = 6379, weight=10, max_fails=3, fail_timeout=10 },\n                }\n            },\n        },\n    }\n\n    _M.api = {\n        enable = false,\n        typ = \"http\",\n            http_opts = {\n            query = \"GET /status HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n\",\n            statuses = {\n                    [\"500\"] = false,\n                    [\"502\"] = false,\n                    [\"503\"] = false,\n                    [\"504\"] = false,\n            },\n        },\n\n        mode = \"hash\",\n\n        cluster = {\n            dc1 = {\n                servers = {\n                    { host = \"192.168.1.1\", port = 1234, weight=10, max_fails=3, fail_timeout=10 },\n                }\n            },\n            dc2 = {\n                servers = {\n                    { host = \"192.168.1.2\", port = 1234, weight=10, max_fails=3, fail_timeout=10 },\n                }\n            }\n        }\n    }\n\n    _M.ups_from_nginx = {\n        timeout = 2,\n\n        cluster = {\n            {   -- level 1\n                upstream = \"api.com\",\n            },\n            {   -- level 2\n                upstream = \"api.com\",\n                upstream_only_backup = true,\n            },\n        },\n    }\n\n    return _M\n```\n\nglobal configurations\n---------------------\n\n* `checkup_timer_interval`: Interval of sending heartbeats to backend servers. Default is `5`.\n* `checkup_timer_overtime`: Interval of checkups to expire the timer key. In most cases, you don't need to change this value. Default is `60`.\n* `default_heartbeat_enable`: Checkups will sent heartbeats to servers by default or not. Default is `true`.\n* `checkup_shd_sync_enable`: Create upstream syncer for each worker. If set to `false`, dynamic upstream will not work properly. Default is `true`.\n* `shd_config_timer_interval`: Interval of syncing upstream list from shared memory. Default is equal to `checkup_timer_interval`.\n* `ups_status_sync_enable`: If set to `true`, checkups will sync upstram status from checkups to Nginx upstream blocks. Default is `false`.\n* `ups_status_timer_interval`: Interval of syncing upstream status from checkups to Nginx upstream blocks.\n\nCluster configurations\n----------------------\n\n* `skey`: `_M.xxxxx`. `xxxxx` is the `skey`(service key) of this Cluster.\n* `enable`: Enable or disable heartbeats to servers. Default is `true`.\n* `typ`: Cluster type, must be one of `general`, `redis`, `mysql`, `http`. Default is `general`.\n    * `general`: Heartbeat by TCP `sock:connect`.\n    * `redis`: Heartbeat by redis `PING`. [lua-resty-redis](https://github.com/openresty/lua-resty-redis) module is required.\n    * `mysql`: Heartbeat by mysql `db:connect`. [lua-resty-mysql](https://github.com/openresty/lua-resty-mysql) module is required.\n    * `http`: Heartbeat by HTTP request. You can setup customized HTTP request and response codes in `http_opts`.\n* `timeout`: Connect timeout to upstream servers. Default is `5`.\n* `read_timeout`: Read timeout to upstream servers (not used during heartbeating). Default is equal to `timeout`.\n* `send_timeout`: Write timeout to upstream servers (not used during heartbeating). Default is equal to `timeout`.\n* `http_opts`: HTTP heartbeat configurations. Only works for `typ=\"http\"`.\n    * `query`: HTTP request to heartbeat.\n    * `statuses`: If the code returned by server is set to `false`, then the server is considered to be failing.\n\n* `mode`: Balance mode. Can be set to `hash`, `url_hash` or `ip_hash`. Checkups will balance servers by `hash_key`, `ngx.var.uri` or `ngx.var.remote_addr`. Default is `wrr`.\n* `protected`: If set to `true` and all the servers in the cluster are failing, checkups will not mark the last failing server as unavailable(`err`), instead, it will be marked as `unstable`(still available in next try). Default is `true`.\n* `cluster`: You can configure multiple levels according to the cluster priority, at each level you can configure a cluster of `servers`. Checkups will try next level only when all the servers in the prior level are consitered unavailable.\n\n    Instead of trying clusters by levels, you can configure checkups trying clusters by key(see `api` cluster above). Remember you should also pass extra argument like `opts.cluster_key={\"dc1\", \"dc2\"}` or `opts.cluster_key={3, 1, 2}` to [checkups.read_ok](#ready_ok) to make checkups trying on the order of `dc1`, `dc2` or `level 3`, `level 1`, `level 2`. If you haven't passed `opts.cluster_key` to [checkups.ready_ok](#ready_ok), checkups will still try clusters by levels. As for the above `api` cluster, checkups will eventually return `no servers available`.\n    * `try`: Retry count. Default is the number of servers.\n    * `try_timeout`: Limits the time during which a request can be responsed, likewise nginx `proxy_next_upstream_timeout`.\n    * `servers`: Configuration for `servers` are listed as follows,\n        * `weight`: Sets the weight of the server. Default is `1`.\n        * `max_fails`: Sets the number of unsuccessful attempts to communicate with the server that should happen in the duration set by the `fail_timeout` parameter. By default, the number of unsuccessful attempts is set to `0`, which disables the accounting of attempts. What is considered an unsuccessful attempt is defined by `http_opts.statuses` if `typ=\"http\"` or a `nil`/`false` returned by [checkups.ready_ok](#ready_ok). This options is only available in round-robin.\n        * `fail_timeout`: Sets the time during which the specified number of unsuccessful attempts to communicate with the server should happen to consider the server unavailable and the period of time the server will be considered unavailable. By default, the parameter is set to `10` seconds. This options is only available in round-robin.\n\n    * `upstream`: Name of Nginx upstream blocks. Checkups will extract servers from Nginx conf's upstream blocks in [prepare_checker](#prepare_checker). [lua-upstream-nginx-module](https://github.com/openresty/lua-upstream-nginx-module) module is required.\n    * `upstream_only_backup`: If set to `true`, checkups will only extract backup servers from Nginx upstream blocks.\n\n\nNginx configuration\n-------------------\n\nAdd pathes of lua config file and checkups to `lua_package_path` and create lua shared dicts used by checkups. You should put these lines into `http` block of your Nginx config file.\n\n    lua_package_path \"/path/to/lua-resty-checkups/lib/?.lua;/path/to/config.lua;;\";\n\n    lua_shared_dict state 10m;\n    lua_shared_dict mutex 1m;\n    lua_shared_dict locks 1m;\n    lua_shared_dict config 10m;\n\nIf you use stream subsystem, you should put these lines into `stream` block of your Nginx config file.\n\n    lua_package_path \"/path/to/lua-resty-checkups/lib/?.lua;/path/to/config.lua;;\";\n\n    lua_shared_dict stream_state 10m;\n    lua_shared_dict stream_mutex 1m;\n    lua_shared_dict stream_locks 1m;\n    lua_shared_dict stream_config 10m;\n\nAPI\n===\n\n![](lua-resty-checkups+API.png)\n\ninit\n---------------\n**syntax:** *init(config)*\n\n**phase:** *init_by_lua*\n\nCopy upstreams from `config.lua` to shdict, extract servers from Nginx upstream blocks and do some basic initialization.\n\n\nprepare_checker\n---------------\n\n**syntax:** *prepare_checker(config)*\n\n**phase:** *init_worker_by_lua*\n\nCopy configurations from `config.lua` to worker checkups, extract servers from Nginx upstream blocks and do some basic initialization.\n\n\ncreate_checker\n--------------\n\n**syntax:** *create_checker()*\n\n**phase:** *init_worker_by_lua*\n\nCreate heartbeat timer and upstream sync timer. Only one heartbeat timer will be created among all the workers. It's highly recommended to call this method in `init_worker` phase.\n\nready_ok\n--------\n\n**syntax:** *res, err = ready_ok(skey, callback, opts?)*\n\n**phase:** *rewrite_by_lua\u0026#42;, access_by_lua\u0026#42;, content_by_lua\u0026#42;, ngx.timer.\u0026#42;*\n\nSelect an available `peer` from cluster `skey` and call `callback(peer.host, peer.port, opts)`.\n\nThe `opts` table accepts the following fields,\n\n* `cluster_key`: Try clusters by `cluster_key`. Checkups will try clusters on the order of `cluster_key`. `clusters_key` can be the name of the clusters or the level of the clusters. clusters eg: `{\"cluster_name_A\", \"name_B\", \"name_C\"}`. levels eg: `{3, 2, 1}`.\n* `hash_key`: Key used in `hash` balance mode. If not set, `ngx.var.uri` will be used.\n* `try`: Retry will be no more than `try` times.\n* `try_timeout`: Limits the time during which a request can be responsed, likewise nginx `proxy_next_upstream_timeout`.\n\nReturns what `callback` returns on success, or returns `nil` and a string describing the error otherwise.\n\nIf `callback` returns `nil` or `false`, checkups will consider it to be a failed try and will retry `callback` with another peer. So, **always remember not to return `nil` or `false` after a successful callback.**\n\nselect_peer\n-----------\n\n**syntax:** *peer, err = select_peer(skey)*\n\n**context:** *rewrite_by_lua\u0026#42;, access_by_lua\u0026#42;, content_by_lua\u0026#42;, balancer_by_lua*\n\nSelect an available peer from cluster `skey`.\n\nReturn a table containing `host` and `port` of an available peer.\n\nIn case of errors, returns nil with a string describing the error.\n\nget_status\n----------\n\n**syntax:** *status = get_status()*\n\n**phase:** *rewrite_by_lua\u0026#42;, access_by_lua\u0026#42;, content_by_lua\u0026#42;, ngx.timer.\u0026#42;*\n\nReturn checkups status in `json` format.\n\nget_ups_timeout\n---------------\n\n**syntax:** *connect_timeout, send_timeout, read_timeout = get_ups_timeout(skey)*\n\n**phase:** *rewrite_by_lua\u0026#42;, access_by_lua\u0026#42;, content_by_lua\u0026#42;, ngx.timer.\u0026#42;*\n\nReturn timeout of cluster `skey`.\n\nfeedback_status\n---------------\n\n**syntax:** *ok, err = feedback_status(skey, host, port, failed)*\n\n**context:** *rewrite_by_lua\u0026#42;, access_by_lua\u0026#42;, content_by_lua\u0026#42;, ngx.timer.\u0026#42;, balancer_by_lua.\u0026#42;*\n\nMark server `host:port` in cluster `skey` as failed(`true`) or available(`false`).\n\nReturns `1` on success, or returns `nil` and a string describing the error otherwise.\n\nupdate_upstream\n---------------\n\n**syntax:** *ok, err = update_upstream(skey, upstream)*\n\n**phase:** *rewrite_by_lua\u0026#42;, access_by_lua\u0026#42;, content_by_lua\u0026#42;, ngx.timer.\u0026#42;*\n\nUpdate cluster `skey`. `upstream` is in the same format as `cluster` in `config.lua`.\n\nReturns `true` on success, or returns `false` and a string describing the error otherwise.\n\ndelete_upstream\n---------------\n\n**syntax:** *ok, err = delete_upstream(skey)*\n\n**phase:** *rewrite_by_lua\u0026#42;, access_by_lua\u0026#42;, content_by_lua\u0026#42;, ngx.timer.\u0026#42;*\n\nDelete cluster `skey` from upstream list.\n\nReturns `true` on success, or returns `false` and a string describing the error otherwise.\n\nCopyright and License\n=====================\n\nThe bundle itself is licensed under the 2-clause BSD license.\n\nCopyright (c) 2016, UPYUN(又拍云) Inc.\n\nThis module is licensed under the terms of the BSD license.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nSee Also\n========\n* [lua-nginx-module](https://github.com/openresty/lua-nginx-module)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupyun%2Flua-resty-checkups","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fupyun%2Flua-resty-checkups","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupyun%2Flua-resty-checkups/lists"}