durpina
具有良好 API 的动态上游反向代理
$ opm get slact/durpina
Durpina
具有良好 API 的动态上游反向代理
一个极其灵活、易于使用的动态 Nginx/OpenResty 上游模块,基于 toruneko 的 lua-resty-upstream。
可配置和可脚本化的负载均衡、服务器健康检查、向上下游添加和删除服务器等等。您无需研究 API 即可使用它,也无需成为 Lua 专家即可编写脚本。
安装
安装 OpenResty,然后使用 opm
工具安装 durpina
opm install slact/durpina
示例配置
#-- nginx.conf:
http {
lua_shared_dict upstream 1m; #-- shared memory to be used by durpina. 1mb should be neough
lua_socket_log_errors off; #-- don't clutter the error log when upstream severs fail
upstream foo {
server localhost:8080; #--default weight is 1
server host1:8080 weight=5;
server host2:8080 weight=7;
balancer_by_lua_block {
--load balance this upstream in round-robin mode
require "durpina.balancer" "round-robin"
--note: the above line is Lua syntax sugar equivalent to
-- require("durpina.balancer")("round-robin")
}
}
#--
upstream bar {
server 0.0.0.0; #-- nginx config syntax needs at least 1 server.
#-- the address 0.0.0.0 is treated by Durpina as a placeholder and is ignored
balancer_by_lua_block {
require "durpina.balancer" "ip-hash"
}
}
init_worker_by_lua_block {
local Upstream = require "durpina.upstream"
--Use the "upstream" lua_shared_dict declared above
--setting the resolver is required for upstream server DNS resolution
Upstream.init("upstream", {resolver="8.8.8.8"})
local upfoo = Upstream.get("foo")
--add a health check to the upstream
upfoo:add_monitor("http", {uri="/still_alive"})
local upbar = Upstream.get("bar")
--this is an upstream with no servers
--peers can be added anytime
upbar:add_peer("localhost:8080 weight=1") --hostnames are resolved once when added, just like Nginx would do
upbar:add_peer({host="10.0.0.2", port=8090, weight=7, fail_timeout=10}) --can be added as a table, too
upbar:add_monitor("tcp", {port=10000}) -- check if able to make tcp connection to server on port 10000
}
server {
#-- here's where we make use of the upstream
listen 80;
location /foo {
proxy_pass http://foo;
}
location /bar {
proxy_pass http://bar;
}
}
server {
#-- upstream info and management
listen 8080;
#-- POST /set_upstream_peer_weight/upstream_name/peer_name
#-- request body is the peer's new weight
location ~/set_upstream_peer_weight/foo/(.*)/(\d+) {
content_by_lua_block {
local Upstream = require "durpina.upstream"
local up = Upstream.get("foo")
local peername = ngx.var[1]
local weight = tonumber(ngx.var[2])
local peer = up:get_peer(peername)
if peer and weight then
peer:set_weight(weight)
ngx.say("weight set!")
else
ngx.status = 404
ngx.say("peer not found or weight invalid")
end
}
}
}
}
API
上游
Upstream = require "durpina.upstream"
Upstream.init(shdict_name, options)
init_worker_by_lua_block {
Upstream.init("myshdict", {resolver="8.8.8.8"})
}
初始化 Durpina 以使用名为 shdict_name
的 `lua_shared_dict`。此调用是必需的,在执行其他任何操作之前,并且必须存在于 `init_worker_by_lua` 字符串、块或文件中。对于大多数设置,大小为 1m 的块就足够了。
options
参数支持以下参数
resolver
:一个字符串或字符串数组,用作 DNS 解析的名称服务器。如果需要在 Nginx 启动后解析服务器主机名,则需要此参数。
Upstream.get(upstream_name)
local upstream = Upstream.get("foo")
返回名为 upstream_name
的上游,其节点根据相应 上游 块的内容初始化。标记为 backup
或地址为 0.0.0.0
的上游节点将被忽略。
upstream.name
此上游的名称。
upstream:get_peer(peer_name)
local peer = upstream:get_peer("localhost:8080")
返回名称为 peer_name
的 节点,如果此上游中不存在这样的 节点,则返回 nil。
upstream:add_peer(peer_config)
local peer, err = upstream:add_peer("localhost:8080 fail_timeout=15 weight=7")
local peer, err = upstream:add_peer({name="localhost:8080", fail_timeout=15, weight=7})
local peer, err = upstream:add_peer({host="localhost", port=8080, fail_timeout=15, weight=7})
将节点添加到上游。peer_config
参数可以是格式化为 `server` 上游指令的字符串,也可以是具有以下键的 Lua 表格:name
(“host:port”)、host
、port
、fail_timeout
、weight
。表格中必须存在 name
或 host
。
上游块中不允许两个节点具有相同的名称。
返回新添加的 节点 或 nil, error
upstream:remove_peer(peer)
local peer = upstream:get_peer("localhost:8080")
loal ok, err = upstream:remove_peer(peer)
从上游中删除 节点。
upstream:get_peers(selector)
local peers = upstream:get_peers("all")
返回与 selector
匹配的 节点 数组,selector
可以是以下之一:nil
(与 all
相同)、"all"
、"failing"
、"down"
、"temporary_down"
、"permanent_down"
。
upstream:add_monitor(name, opts)
local ok, err = upstream:add_monitor("http", {url="/health_check"})
向上下游添加 `monitor`。监控器定期检查每个节点的健康状况,并在“监控器”部分中详细讨论。
upstream:info()
print(upstream:info())
/* output */
{
"name":"weighted_roundrobin",
"revision":2,
"peers":[{
"name":"localhost:8083",
"address":"127.0.0.1",
"weight":1,
"state":"up"
},{
"name":"127.0.0.1:8084",
"address":"127.0.0.1",
"weight":10,
"state":"failing"
},{
"name":"127.0.0.1:8085",
"address":"127.0.0.1",
"weight":15,
"state":"down"
}],
"monitors":[{
"id":"http",
"name":"http"
}]
}
返回包含有关此上游状态信息的 JSON 字符串。
节点
节点是 上游 中的服务器。它们是在内部初始化的——尽管有一个 Peer.new 方法,但您确实不应该使用它。相反,节点是使用 `upstream:add_peer()` 创建的,并且通过从 上游块 加载创建的。
local peer = upstream:get_peer("127.0.0.1")
peer.name
节点的名称,格式为 "hostname:port"
peer.port
端口,显然。
peer.initial_weight
节点最初加载的权重,未受后续对 `peer:set_weight(n)` 的调用的修改
peer:get_address()
local address, err = peer:get_address()
如果节点已解析,则返回节点地址。如果地址不可用或 DNS 解析失败,则返回 nil, err
。
peer:get_weight()
local weight = peer:get_weight()
返回节点的当前权重。
peer:set_weight(weight)
local ok, err = peer:set_weight(15)
为所有 Nginx 工作进程设置节点的当前权重。权重必须是正整数。
peer:get_upstream()
local upstream = peer:get_upstream()
返回此节点的 `upstream`。
peer:set_state(state)
peer:set_state("down")
设置节点的状态,在所有 Nginx 工作进程之间共享。可以是 up
、down
或 temporary_down
之一
peer:is_down(kind)
如果节点已关闭,则返回 true
。参数 kind
可以是 nil
或 "any"
、"permanent"
或 "temporary"
之一,并反映节点所处的关闭状态类型。kind
的默认值为 "any"
。
peer:is_failing()
如果节点当前正在失败,则返回 true
;也就是说,如果它在过去 fail_timeout
时间间隔内记录了不止一次失败。
peer:add_fail()
将节点的故障计数器加 1。此计数器在所有 Nginx 工作进程之间共享。
peer:resolve(force)
如有必要,将节点主机名解析为其地址。如果 force
为 true,则覆盖现有的地址(如果存在)。与其他 peer
更新一样,新解析的地址会自动在 Nginx 工作进程之间共享。
为了使节点 DNS 解析工作,必须向 Upstream.init() 提供 resolver
。
均衡器
require "durpina.balancer"
均衡器是在 上游块 中使用 `balancer_by_lua` 块调用的
upstream foo {
localhost:8080 weight=2;
localhost:8081;
balancer_by_lua_block {
require "durpina.balancer" "round-robin"
--this is syntactic sugar equivalent to
-- require("durpina.balancer").balance("round-robin")
}
}
Balancer(algorithm, args...)
Balancer.balance(algorithm, args...)
Balancer.balance(algorithm)
使用指定的 algorithm
平衡上游,支持以下算法
"
round-robin
"(加权)"
unweighted-round-robin
""
ip-hash
",基于源 IP 的一致路由"
consistent-hash
",基于自定义请求变量的一致路由
args...
参数将直接传递给均衡器。目前只有 consistent-hash
算法需要参数,即要散列的值
balancer_by_lua_block {
--load-balance by the first regex capture in the request url
require "durpina.balancer" ("consistent-hash", ngx.var[1])
}
监控器
upstream:add_monitor(name, opts)
监控器添加到上游以检查节点的健康状态并运行定期维护任务。监控器不会直接初始化,而是通过 `upstream:add_monitor()` 调用添加。
监控器 name
标识正在添加的监控器类型。"多个监控器" 已包含在内,更多监控器可以使用 `Monitor.register()` 添加。
每个新监控器都将传递 opts
选项表。此表只能包含数字或字符串值。所有监控器都处理 opts
键 id
,该键唯一标识上游中的监控器。在不存在时,id
默认为监控器 name
。因此,要拥有多个 http
监控器,至少必须为其中一个提供 id
upstream:add_monitor("http") --pings the root url
upstream:add_monitor("http", {id="http_hello", url="/hello", interval=30})
总共,以下 opts
由所有监控器使用
id
**: 唯一标识监控器。默认值:监控器名称interval
**: 每次检查之间的时间。在每个间隔结束时检查一个节点,在所有 Nginx 工作进程之间分配。可以是数字或 Nginx 时间字符串(“10s”、“30m”等)。默认值:Monitor.default_interval
(5 秒)port
**: 通过连接到此端口而不是节点的上游端口来执行监控器检查。peers
**: 要检查的节点类型。可以是 `upstream:get_peers()` 中的选择器之一。默认值:"all"
预定义监控器
http
发送 HTTP 请求,如果请求失败则添加故障。
upstream:add_monitor("http", {id="http-hello", url="/hello", ok_codes="2xx", interval="5m"})
opts
:
url
**: /path/to/request 默认值:"/"
ok_codes
**: 被视为“ok”的响应代码。包含代码数字和“Nxx”表示法的空格分隔字符串。默认值:"101 102 2xx 3xx"
header_*
**: 以“header_”为前缀的所有 opts 成为请求头method
**: 请求方法。默认值:"GET"
body
**: 请求正文。默认值:nil
tcp
尝试通过 TCP 套接字连接到服务器,如果连接失败则添加故障。
upstream:add_monitor("tcp", {id="tcp-ping", timeout=200})
opts
:
timeout
**: 连接超时,以毫秒为单位。默认值:OpenResty 默认值
haproxy-agent-check
尝试通过 TCP 连接到节点并读取一行文本。数据根据 HAProxy agent-check 规范进行处理。状态“drain”和“maint”被视为“down”,而“up”和“ready”都被视为“up”。
upstream:add_monitor("haproxy-agent-check", {timeout=200})
opts
**:
timeout
**: 连接超时,以毫秒为单位。默认值:OpenResty 默认值
http-haproxy-agent-check
与 haproxy-agent-check 相同,但通过 HTTP。
upstream:add_monitor("haproxy-agent-check", {url="/haproxy_agent_status"})
opts
:
url
**: /path/to/request 默认值:"/"
ok_codes
**: 被视为“ok”的响应代码。包含代码数字和“Nxx”表示法的空格分隔字符串。默认值:“101 102 2xx 3xx”header_*
**: 以“header_”为前缀的所有 opts 成为请求头
注册新监控器
新的监控器使用 Monitor.register
添加
Monitor.register(name, check)
Monitor.register("fancy_monitor", check_function)
-- or --
Monitor.register("fancy_monitor", check_table)
按名称注册一个监控器,以便稍后添加到上游。Check
可以是表格或函数
init_worker_by_lua_block {
-- register as a table
Monitor.register("mymonitor", {
init = initialization_function, -- (optional)
check = peer_checking_function, --(required)
interval = default interval for this monitor --(optional)
}
--register as a function
Monitor.register("mymonitor", peer_checking_function)
-- is equivalent to --
Monitor.register("mymonitor", {
check = peer_checking_function
})
}
monitor check_table.init
每次将监控器添加到上游时都会调用 init
函数。它负责初始化监控器状态并验证 opts
。它的签名如下
function init_monitor(upstream, shared, local_state)
参数如下
upstream
** 此监控器正在添加到其中的 上游。shared
** 是一个 openresty 共享字典,命名空间为此监控器实例。local_state
** 是一个工作进程本地表格,用于跟踪执行状态、缓存和配置。它初始化为传递给 upstream:add_monitor() 的opts
表格的副本
monitor check_table.check
在配置的间隔内,每次对后续的每个节点都会调用 check
函数。它负责使用 `peer:set_state()` 和其他 `peer` 函数更改节点状态。它的签名如下
function check_monitor(upstream, peer, shdict, local_state)
参数如下
upstream
** 此监控器正在添加到其中的 上游。peer
** 需要检查的 节点。shared
** 是一个 openresty 共享字典,命名空间为此监控器实例。local_state
** 是一个工作进程本地表格,用于跟踪执行状态、缓存和配置。它初始化为传递给 upstream:add_monitor() 的opts
表格的副本
有关如何创建监控器的更多详细信息将在以后添加。
POD 错误
嗨!以上文档存在一些编码错误,如下所述:
- 大约在第 605 行
-
未终止的 B<...> 序列
- 大约在第 610 行
-
未终止的 B<...> 序列
- 大约在第 617 行
-
未终止的 B<...> 序列
- 大约在第 622 行
-
未终止的 B<...> 序列
- 大约在第 649 行
-
未终止的 B<...> 序列
- 大约在第 654 行
-
未终止的 B<...> 序列
- 大约在第 659 行
-
未终止的 B<...> 序列
- 大约在第 663 行
-
未终止的 B<...> 序列
- 大约在第 668 行
-
未终止的 B<...> 序列
- 大约在第 690 行
-
未终止的 B<...> 序列
- 大约在第 709 行
-
未终止的 B<...> 序列
- 大约在第 714 行
-
未终止的 B<...> 序列
- 大约在第 736 行
-
未终止的 B<...> 序列
- 大约在第 741 行
-
未终止的 B<...> 序列
- 大约在第 746 行
-
未终止的 B<...> 序列
- 大约在第 798 行
-
未终止的 B<...> 序列
- 大约在第 802 行
-
未终止的 B<...> 序列
- 大约在第 806 行
-
未终止的 B<...> 序列
- 大约在第 825 行
-
未终止的 B<...> 序列
- 大约在第 829 行
-
未终止的 B<...> 序列
- 大约在第 833 行
-
未终止的 B<...> 序列
- 大约在第 837 行
-
未终止的 B<...> 序列
作者
slact
许可证
mit