This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new a7c040f feat(plugin): add degradation switch for traffic control
plugin (#4774)
a7c040f is described below
commit a7c040f117ef9d21e88ebe2d01b04a03d008a115
Author: okaybase <[email protected]>
AuthorDate: Sun Aug 8 22:42:54 2021 +0800
feat(plugin): add degradation switch for traffic control plugin (#4774)
Co-authored-by: nic-chen <[email protected]>
Co-authored-by: lixingwang <[email protected]>
---
apisix/plugins/limit-conn.lua | 1 +
apisix/plugins/limit-conn/init.lua | 8 ++-
apisix/plugins/limit-count.lua | 9 +++-
.../limit-count/limit-count-redis-cluster.lua | 7 ++-
apisix/plugins/limit-count/limit-count-redis.lua | 8 ++-
apisix/plugins/limit-req.lua | 7 +++
docs/en/latest/plugins/limit-conn.md | 1 +
docs/en/latest/plugins/limit-count.md | 1 +
docs/en/latest/plugins/limit-req.md | 1 +
docs/zh/latest/plugins/limit-conn.md | 1 +
docs/zh/latest/plugins/limit-count.md | 1 +
docs/zh/latest/plugins/limit-req.md | 1 +
t/plugin/limit-conn.t | 47 +++++++++++++++++-
t/plugin/limit-count-redis-cluster.t | 57 ++++++++++++++++++++++
t/plugin/limit-count-redis2.t | 55 +++++++++++++++++++++
15 files changed, 198 insertions(+), 7 deletions(-)
diff --git a/apisix/plugins/limit-conn.lua b/apisix/plugins/limit-conn.lua
index 40a85c1..62c0015 100644
--- a/apisix/plugins/limit-conn.lua
+++ b/apisix/plugins/limit-conn.lua
@@ -33,6 +33,7 @@ local schema = {
rejected_code = {
type = "integer", minimum = 200, maximum = 599, default = 503
},
+ allow_degradation = {type = "boolean", default = false}
},
required = {"conn", "burst", "default_conn_delay", "key"}
}
diff --git a/apisix/plugins/limit-conn/init.lua
b/apisix/plugins/limit-conn/init.lua
index 16a7717..95410dd 100644
--- a/apisix/plugins/limit-conn/init.lua
+++ b/apisix/plugins/limit-conn/init.lua
@@ -41,6 +41,9 @@ function _M.increase(conf, ctx)
local lim, err = lrucache(conf, nil, create_limit_obj, conf)
if not lim then
core.log.error("failed to instantiate a resty.limit.conn object: ",
err)
+ if conf.allow_degradation then
+ return
+ end
return 500
end
@@ -53,7 +56,10 @@ function _M.increase(conf, ctx)
return conf.rejected_code or 503
end
- core.log.error("failed to limit req: ", err)
+ core.log.error("failed to limit conn: ", err)
+ if conf.allow_degradation then
+ return
+ end
return 500
end
diff --git a/apisix/plugins/limit-count.lua b/apisix/plugins/limit-count.lua
index f5a10de..ac0f603 100644
--- a/apisix/plugins/limit-count.lua
+++ b/apisix/plugins/limit-count.lua
@@ -49,7 +49,8 @@ local schema = {
type = "string",
enum = {"local", "redis", "redis-cluster"},
default = "local",
- }
+ },
+ allow_degradation = {type = "boolean", default = false}
},
required = {"count", "time_window"},
dependencies = {
@@ -160,6 +161,9 @@ function _M.access(conf, ctx)
local lim, err = core.lrucache.plugin_ctx(lrucache, ctx, conf.policy,
create_limit_obj, conf)
if not lim then
core.log.error("failed to fetch limit.count object: ", err)
+ if conf.allow_degradation then
+ return
+ end
return 500
end
@@ -174,6 +178,9 @@ function _M.access(conf, ctx)
end
core.log.error("failed to limit req: ", err)
+ if conf.allow_degradation then
+ return
+ end
return 500, {error_msg = "failed to limit count: " .. err}
end
diff --git a/apisix/plugins/limit-count/limit-count-redis-cluster.lua
b/apisix/plugins/limit-count/limit-count-redis-cluster.lua
index b4dfc54..935adee 100644
--- a/apisix/plugins/limit-count/limit-count-redis-cluster.lua
+++ b/apisix/plugins/limit-count/limit-count-redis-cluster.lua
@@ -74,8 +74,11 @@ function _M.new(plugin_name, limit, window, conf)
end
local self = {
- limit = limit, window = window, conf = conf,
- plugin_name = plugin_name, red_cli =red_cli
+ limit = limit,
+ window = window,
+ conf = conf,
+ plugin_name = plugin_name,
+ red_cli = red_cli,
}
return setmetatable(self, mt)
diff --git a/apisix/plugins/limit-count/limit-count-redis.lua
b/apisix/plugins/limit-count/limit-count-redis.lua
index 253d557..5cd2286 100644
--- a/apisix/plugins/limit-count/limit-count-redis.lua
+++ b/apisix/plugins/limit-count/limit-count-redis.lua
@@ -41,8 +41,12 @@ local script = [=[
function _M.new(plugin_name, limit, window, conf)
assert(limit > 0 and window > 0)
- local self = {limit = limit, window = window, conf = conf,
- plugin_name = plugin_name}
+ local self = {
+ limit = limit,
+ window = window,
+ conf = conf,
+ plugin_name = plugin_name,
+ }
return setmetatable(self, mt)
end
diff --git a/apisix/plugins/limit-req.lua b/apisix/plugins/limit-req.lua
index 2577f7c..829ff53 100644
--- a/apisix/plugins/limit-req.lua
+++ b/apisix/plugins/limit-req.lua
@@ -39,6 +39,7 @@ local schema = {
nodelay = {
type = "boolean", default = false
},
+ allow_degradation = {type = "boolean", default = false}
},
required = {"rate", "burst", "key"}
}
@@ -73,6 +74,9 @@ function _M.access(conf, ctx)
create_limit_obj, conf)
if not lim then
core.log.error("failed to instantiate a resty.limit.req object: ", err)
+ if conf.allow_degradation then
+ return
+ end
return 500
end
@@ -96,6 +100,9 @@ function _M.access(conf, ctx)
end
core.log.error("failed to limit req: ", err)
+ if conf.allow_degradation then
+ return
+ end
return 500
end
diff --git a/docs/en/latest/plugins/limit-conn.md
b/docs/en/latest/plugins/limit-conn.md
index bba984e..f665b32 100644
--- a/docs/en/latest/plugins/limit-conn.md
+++ b/docs/en/latest/plugins/limit-conn.md
@@ -43,6 +43,7 @@ Limiting request concurrency plugin.
| only_use_default_delay | boolean | optional | false | [true,false]
|
enable the strict mode of the latency seconds. If you set this option to
`true`, it will run strictly according to the latency seconds you set without
additional calculation logic.
[...]
| key | object | required | | ["remote_addr",
"server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"] | to
limit the concurrency level. <br />For example, one can use the host name (or
server zone) as the key so that we limit concurrency per host name. Otherwise,
we can also use the client address as the key so that we can avoid a single
client from flooding our service with too many parallel connections or
requests. <br /> Now accept those as [...]
| rejected_code | string | optional | 503 | [200,...,599]
| returned
when the request exceeds `conn` + `burst` will be rejected.
[...]
+| allow_degradation | boolean | optional
| false |
| Whether to enable plugin degradation when the limit-conn function
is temporarily unavailable. Allow requests to continue when the value is set to
true, default false. |
**Key can be customized by the user, only need to modify a line of code of the
plug-in to complete. It is a security consideration that is not open in the
plugin.**
diff --git a/docs/en/latest/plugins/limit-count.md
b/docs/en/latest/plugins/limit-count.md
index cbadf02..b178ac5 100644
--- a/docs/en/latest/plugins/limit-count.md
+++ b/docs/en/latest/plugins/limit-count.md
@@ -42,6 +42,7 @@ Limit request rate by a fixed number of requests in a given
time window.
| key | string | optional |
"remote_addr" | ["remote_addr", "server_addr", "http_x_real_ip",
"http_x_forwarded_for", "consumer_name", "service_id"] | The user specified key
to limit the count. <br /> Now accept those as key: "remote_addr"(client's IP),
"server_addr"(server's IP), "X-Forwarded-For/X-Real-IP" in request header,
"consumer_name"(consumer's username) and "service_id".
|
| rejected_code | integer | optional |
503 | [200,...,599]
| The HTTP status code returned when
the request exceeds the threshold is rejected, default 503.
|
| policy | string | optional |
"local" | ["local", "redis", "redis-cluster"]
| The rate-limiting policies to use for
retrieving and incrementing the limits. Available values are `local`(the
counters will be stored locally in-memory on the node), `redis`(counters are
stored on a Redis server and will be shared across the nodes, usually use it to
do the global speed limit) [...]
+| allow_degradation | boolean | optional
| false |
| Whether to enable plugin degradation when the limit-count function
is temporarily unavailable(e.g. redis timeout). Allow requests to continue when
the value is set to true, default false. |
| redis_host | string | required for `redis` |
|
| When using the `redis` policy, this
property specifies the address of the Redis server.
|
| redis_port | integer | optional |
6379 | [1,...]
| When using the `redis` policy, this
property specifies the port of the Redis server.
|
| redis_password | string | optional |
|
| When using the `redis` policy, this
property specifies the password of the Redis server.
|
diff --git a/docs/en/latest/plugins/limit-req.md
b/docs/en/latest/plugins/limit-req.md
index 977dfb4..a52b17d 100644
--- a/docs/en/latest/plugins/limit-req.md
+++ b/docs/en/latest/plugins/limit-req.md
@@ -43,6 +43,7 @@ limit request rate using the "leaky bucket" method.
| key | string | required | | ["remote_addr",
"server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"] | the
user specified key to limit the rate, now accept those as key:
"remote_addr"(client's IP), "server_addr"(server's IP),
"X-Forwarded-For/X-Real-IP" in request header, "consumer_name"(consumer's
username). |
| rejected_code | integer | optional | 503 | [200,...,599]
| The HTTP status code returned
when the request exceeds the threshold is rejected.
|
| nodelay | boolean | optional | false |
| If nodelay flag is true,
bursted requests will not get delayed |
+| allow_degradation | boolean | optional
| false |
| Whether to enable plugin degradation when the limit-req function
is temporarily unavailable. Allow requests to continue when the value is set to
true, default false. |
**Key can be customized by the user, only need to modify a line of code of the
plug-in to complete. It is a security consideration that is not open in the
plugin.**
diff --git a/docs/zh/latest/plugins/limit-conn.md
b/docs/zh/latest/plugins/limit-conn.md
index 7dfb2fa..34838fe 100644
--- a/docs/zh/latest/plugins/limit-conn.md
+++ b/docs/zh/latest/plugins/limit-conn.md
@@ -33,6 +33,7 @@ title: limit-conn
| only_use_default_delay | boolean | optional | false | [true,false]
|
延迟时间的严格模式。 如果设置为`true`的话,将会严格按照设置的时间来进行延迟
[...]
| key | object | required | | ["remote_addr",
"server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"] |
用户指定的限制并发级别的关键字,可以是客户端 IP 或服务端 IP。<br />例如,可以使用主机名(或服务器区域)作为关键字,以便限制每个主机名的并发性。
否则,我们也可以使用客户端地址作为关键字,这样我们就可以避免单个客户端用太多的并行连接或请求淹没我们的服务。 <br />当前接受的 key
有:"remote_addr"(客户端 IP 地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For" 或
"X-Real-IP", "consumer_name"(consumer 的 username)。 |
| rejected_code | string | optional | 503 | [200,...,599]
| 当请求超过 `conn`
+ `burst` 这个阈值时,返回的 HTTP 状态码
[...]
+| allow_degradation | boolean | 可选
| false |
| 当插件功能临时不可用时是否允许请求继续。当值设置为 true 时则自动允许请求继续,默认值是 false。|
**注:key 是可以被用户自定义的,只需要修改插件的一行代码即可完成。并没有在插件中放开是处于安全的考虑。**
diff --git a/docs/zh/latest/plugins/limit-count.md
b/docs/zh/latest/plugins/limit-count.md
index 2539286..fa78406 100644
--- a/docs/zh/latest/plugins/limit-count.md
+++ b/docs/zh/latest/plugins/limit-count.md
@@ -45,6 +45,7 @@ title: limit-count
| key | string | 可选 |
"remote_addr" | ["remote_addr", "server_addr", "http_x_real_ip",
"http_x_forwarded_for", "consumer_name", "service_id"] | 用来做请求计数的有效值。<br
/>例如,可以使用主机名(或服务器区域)作为关键字,以便限制每个主机名规定时间内的请求次数。我们也可以使用客户端地址作为关键字,这样我们就可以避免单个客户端规定时间内多次的连接我们的服务。<br
/>当前接受的 key 有:"remote_addr"(客户端 IP 地址), "server_addr"(服务端 IP 地址),
请求头中的"X-Forwarded-For" 或 "X-Real-IP", "consumer_name"(consumer 的 username),
"service_id" 。 |
| rejected_code | integer | 可选 | 503
| [200,...,599]
| 当请求超过阈值被拒绝时,返回的 HTTP 状态码
[...]
| policy | string | 可选 | "local"
| ["local", "redis", "redis-cluster"]
|
用于检索和增加限制的速率限制策略。可选的值有:`local`(计数器被以内存方式保存在节点本地,默认选项) 和 `redis`(计数器保存在 Redis
服务节点上,从而可以跨节点共享结果,通常用它来完成全局限速);以及`redis-cluster`,跟 redis 功能一样,只是使用 redis 集群方式。
|
+| allow_degradation | boolean | 可选
| false |
| 当限流插件功能临时不可用时(例如,Redis 超时)是否允许请求继续。当值设置为 true 时则自动允许请求继续,默认值是 false。|
| redis_host | string | `redis` 必须 |
|
| 当使用 `redis` 限速策略时,该属性是 Redis 服务节点的地址。
[...]
| redis_port | integer | 可选 | 6379
| [1,...]
| 当使用 `redis` 限速策略时,该属性是 Redis 服务节点的端口
[...]
| redis_password | string | 可选 |
|
| 当使用 `redis` 限速策略时,该属性是 Redis 服务节点的密码。
[...]
diff --git a/docs/zh/latest/plugins/limit-req.md
b/docs/zh/latest/plugins/limit-req.md
index 891cec6..2284120 100644
--- a/docs/zh/latest/plugins/limit-req.md
+++ b/docs/zh/latest/plugins/limit-req.md
@@ -43,6 +43,7 @@ title: limit-req
| key | string | 必须 | | ["remote_addr", "server_addr",
"http_x_real_ip", "http_x_forwarded_for", "consumer_name"] | 用来做请求计数的依据,当前接受的
key 有:"remote_addr"(客户端IP地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For"
或 "X-Real-IP","consumer_name"(consumer 的 username)。 |
| rejected_code | integer | 可选 | 503 | [200,...,599]
| 当请求超过阈值被拒绝时,返回的 HTTP 状态码。
|
| nodelay | boolean | 可选 | false |
| 如果 nodelay 为 true, 请求速率超过 `rate` 但没有超过
(`rate` + `brust`)的请求不会加上延迟, 如果是 false,则会加上延迟。 |
+| allow_degradation | boolean | 可选
| false |
| 当限速插件功能临时不可用时是否允许请求继续。当值设置为 true 时则自动允许请求继续,默认值是 false。|
**key 是可以被用户自定义的,只需要修改插件的一行代码即可完成。并没有在插件中放开是处于安全的考虑。**
diff --git a/t/plugin/limit-conn.t b/t/plugin/limit-conn.t
index c4089b0..fe480f6 100644
--- a/t/plugin/limit-conn.t
+++ b/t/plugin/limit-conn.t
@@ -1242,7 +1242,8 @@ done
"burst": 0,
"default_conn_delay": 0.3,
"rejected_code": 503,
- "key": "remote_addr"
+ "key": "remote_addr",
+ "allow_degradation": true
}
},
"upstream": {
@@ -1283,3 +1284,47 @@ passed
]
--- no_error_log
[error]
+
+
+
+=== TEST 34: invalid route: wrong allow_degradation
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "limit-conn": {
+ "conn": 1,
+ "burst": 1,
+ "default_conn_delay": 0.1,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ "allow_degradation": "true1"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/limit_conn"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.print(body)
+ }
+ }
+--- request
+GET /t
+--- error_code: 400
+--- response_body
+{"error_msg":"failed to check the configuration of plugin limit-conn err:
property \"allow_degradation\" validation failed: wrong type: expected boolean,
got string"}
+--- no_error_log
+[error]
diff --git a/t/plugin/limit-count-redis-cluster.t
b/t/plugin/limit-count-redis-cluster.t
index 70b9371..0eb2d48 100644
--- a/t/plugin/limit-count-redis-cluster.t
+++ b/t/plugin/limit-count-redis-cluster.t
@@ -377,3 +377,60 @@ code: 503
code: 503
--- no_error_log
[error]
+
+
+
+=== TEST 10: set route, four redis nodes, no one is valid, with enable
degradation switch
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/hello",
+ "plugins": {
+ "limit-count": {
+ "count": 9999,
+ "time_window": 60,
+ "key": "http_x_real_ip",
+ "policy": "redis-cluster",
+ "allow_degradation": true,
+ "redis_cluster_nodes": [
+ "127.0.0.1:8001",
+ "127.0.0.1:8002",
+ "127.0.0.1:8003",
+ "127.0.0.1:8004"
+ ],
+ "redis_cluster_name": "redis-cluster-1"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 11: enable degradation switch for TEST 10
+--- request
+GET /hello
+--- response_body
+hello world
diff --git a/t/plugin/limit-count-redis2.t b/t/plugin/limit-count-redis2.t
index 96d4915..d620ef0 100644
--- a/t/plugin/limit-count-redis2.t
+++ b/t/plugin/limit-count-redis2.t
@@ -188,3 +188,58 @@ passed
[200, 200, 503, 503]
--- no_error_log
[error]
+
+
+
+=== TEST 6: set route, with redis host but wrong port, with enable degradation
switch
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/hello",
+ "plugins": {
+ "limit-count": {
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ "policy": "redis",
+ "allow_degradation": true,
+ "redis_host": "127.0.0.1",
+ "redis_port": 16379,
+ "redis_database": 1,
+ "redis_timeout": 1001
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 7: enable degradation switch for TEST 6
+--- request
+GET /hello
+--- response_body
+hello world