You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2021/11/02 12:38:34 UTC

[apisix] branch master updated: feat(limit-conn): support multiple variables as key (#5354)

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 7b06fcc  feat(limit-conn): support multiple variables as key (#5354)
7b06fcc is described below

commit 7b06fcc46874cd1ecb6a6ff33a29ccaed4bf8b29
Author: Xunzhuo <mi...@gmail.com>
AuthorDate: Tue Nov 2 20:38:28 2021 +0800

    feat(limit-conn): support multiple variables as key (#5354)
---
 apisix/plugins/limit-conn.lua        |   7 +-
 apisix/plugins/limit-conn/init.lua   |  25 ++++++-
 docs/en/latest/plugins/limit-conn.md |   3 +-
 docs/zh/latest/plugins/limit-conn.md |   3 +-
 t/plugin/limit-conn2.t               | 131 +++++++++++++++++++++++++++++++++++
 5 files changed, 163 insertions(+), 6 deletions(-)

diff --git a/apisix/plugins/limit-conn.lua b/apisix/plugins/limit-conn.lua
index d43737f..d8389b7 100644
--- a/apisix/plugins/limit-conn.lua
+++ b/apisix/plugins/limit-conn.lua
@@ -26,9 +26,10 @@ local schema = {
         burst = {type = "integer",  minimum = 0},
         default_conn_delay = {type = "number", exclusiveMinimum = 0},
         only_use_default_delay = {type = "boolean", default = false},
-        key = {type = "string",
-            enum = {"remote_addr", "server_addr", "http_x_real_ip",
-                    "http_x_forwarded_for", "consumer_name"},
+        key = {type = "string"},
+        key_type = {type = "string",
+            enum = {"var", "var_combination"},
+            default = "var",
         },
         rejected_code = {
             type = "integer", minimum = 200, maximum = 599, default = 503
diff --git a/apisix/plugins/limit-conn/init.lua b/apisix/plugins/limit-conn/init.lua
index 02b77f1..adc1e68 100644
--- a/apisix/plugins/limit-conn/init.lua
+++ b/apisix/plugins/limit-conn/init.lua
@@ -47,7 +47,30 @@ function _M.increase(conf, ctx)
         return 500
     end
 
-    local key = (ctx.var[conf.key] or "") .. ctx.conf_type .. ctx.conf_version
+    local conf_key = conf.key
+    local key
+    if conf.key_type == "var_combination" then
+        local err, n_resolved
+        key, err, n_resolved = core.utils.resolve_var(conf_key, ctx.var);
+        if err then
+            core.log.error("could not resolve vars in ", conf_key, " error: ", err)
+        end
+
+        if n_resolved == 0 then
+            key = nil
+        end
+    else
+        key = ctx.var[conf_key]
+    end
+
+    if key == nil then
+        core.log.info("bypass the limit conn as the key is empty")
+        -- Bypass the limit conn when the key is empty.
+        -- This behavior is the same as Nginx
+        return
+    end
+
+    key = key .. ctx.conf_type .. ctx.conf_version
     core.log.info("limit key: ", key)
 
     local delay, err = lim:incoming(key, true)
diff --git a/docs/en/latest/plugins/limit-conn.md b/docs/en/latest/plugins/limit-conn.md
index dc01c74..1af26f3 100644
--- a/docs/en/latest/plugins/limit-conn.md
+++ b/docs/en/latest/plugins/limit-conn.md
@@ -41,7 +41,8 @@ Limiting request concurrency plugin.
 | burst              | integer | required    |         | burst >= 0                                                                                | the number of excessive concurrent requests (or connections) allowed to be delayed.                                                                                                                                                                                                                                                                     [...]
 | default_conn_delay | number  | required    |         | default_conn_delay > 0                                                                    | the latency seconds of request when concurrent requests exceeding `conn` but below (`conn` + `burst`).                                                                                                                                                                                                                                                  [...]
 | 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 [...]
+| key_type      | string  | optional    |   "var"   | ["var", "var_combination"] | the type of key. |
+| key           | string  | required    |         |  | the user specified key to limit the rate. If the `key_type` is "var", the key will be treated as a name of variable, like "remote_addr" or "consumer_name". If the `key_type` is "var_combination", the key will be a combination of variables, like "$remote_addr $consumer_name". |
 | rejected_code      | string  | optional    | 503     | [200,...,599]                                                                             | the HTTP status code returned when the request exceeds `conn` + `burst` will be rejected.                                                                                                                                                                                                                                                               [...]
 | rejected_msg       | string | optional                                |            | non-empty                                | the response body 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. |
diff --git a/docs/zh/latest/plugins/limit-conn.md b/docs/zh/latest/plugins/limit-conn.md
index 5f66480..0438229 100644
--- a/docs/zh/latest/plugins/limit-conn.md
+++ b/docs/zh/latest/plugins/limit-conn.md
@@ -31,7 +31,8 @@ title: limit-conn
 | burst              | integer | required |        | burst >= 0                                                                                | 允许被延迟处理的并发请求数。                                                                                                                                                                                                                                                                                                                                              [...]
 | default_conn_delay | number  | required |        | default_conn_delay > 0                                                                    | 默认的典型连接(或请求)的处理延迟时间。                                                                                                                                                                                                                                                                                                                                        [...]
 | 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)。 |
+| key_type      | string | 可选   |  "var"      | ["var", "var_combination"]                                          | key 的类型 |
+| key           | string  | 必须   |        |  | 用来做请求计数的依据。如果 `key_type` 为 "var",那么 key 会被当作变量名称,如 "remote_addr" 和 "consumer_name"。如果 `key_type` 为 "var_combination",那么 key 会当作变量组合,如 "$remote_addr $consumer_name"。 |
 | rejected_code      | string  | optional | 503    | [200,...,599]                                                                             | 当请求超过 `conn` + `burst` 这个阈值时,返回的 HTTP 状态码                                                                                                                                                                                                                                                                                                                   [...]
 | rejected_msg       | string | 可选                                |            | 非空                                          | 当请求超过 `conn` + `burst` 这个阈值时,返回的响应体。                                                                                                                                                                                                             |
 | allow_degradation              | boolean  | 可选                                | false       |                                                                     | 当插件功能临时不可用时是否允许请求继续。当值设置为 true 时则自动允许请求继续,默认值是 false。|
diff --git a/t/plugin/limit-conn2.t b/t/plugin/limit-conn2.t
index 8eec44d..187e3aa 100644
--- a/t/plugin/limit-conn2.t
+++ b/t/plugin/limit-conn2.t
@@ -308,3 +308,134 @@ request latency is nil
 --- error_code: 400
 --- response_body
 {"error_msg":"failed to check the configuration of plugin limit-conn err: property \"rejected_msg\" validation failed: string too short, expected at least 1, got 0"}
+
+
+
+=== TEST 9: set key type to var_combination
+--- 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": 2,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "$http_a $http_b",
+                                "key_type": "var_combination"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]],
+                [[{
+                    "node": {
+                        "value": {
+                            "plugins": {
+                                "limit-conn": {
+                                    "conn": 2,
+                                    "burst": 1,
+                                    "default_conn_delay": 0.1,
+                                    "rejected_code": 503,
+                                    "key": "$http_a $http_b",
+                                    "key_type": "var_combination"
+                                }
+                            },
+                            "upstream": {
+                                "nodes": {
+                                    "127.0.0.1:1980": 1
+                                },
+                                "type": "roundrobin"
+                            },
+                            "uri": "/limit_conn"
+                        },
+                        "key": "/apisix/routes/1"
+                    },
+                    "action": "set"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 10: Don't exceed the burst
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require "t.toolkit.json"
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/limit_conn"
+            local ress = {}
+            for i = 1, 2 do
+                local httpc = http.new()
+                local res, err = httpc:request_uri(uri, {headers = {a = i}})
+                if not res then
+                    ngx.say(err)
+                    return
+                end
+                table.insert(ress, res.status)
+            end
+            ngx.say(json.encode(ress))
+        }
+    }
+--- request
+GET /t
+--- timeout: 10s
+--- response_body
+[200,200]
+--- no_error_log
+[error]
+
+
+
+=== TEST 11: bypass empty key
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require "t.toolkit.json"
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/limit_conn"
+            local ress = {}
+            for i = 1, 2 do
+                local httpc = http.new()
+                local res, err = httpc:request_uri(uri)
+                if not res then
+                    ngx.say(err)
+                    return
+                end
+                table.insert(ress, res.status)
+            end
+            ngx.say(json.encode(ress))
+        }
+    }
+--- request
+GET /t
+--- no_error_log
+[error]
+--- response_body
+[200,200]
+--- error_log
+bypass the limit conn as the key is empty