This is an automated email from the ASF dual-hosted git repository.

shreemaanabhishek 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 7e907a565 feat: add redis and redis-cluster in limit-conn (#10866)
7e907a565 is described below

commit 7e907a565b9302e70daf294398722377fd64c482
Author: theweakgod <[email protected]>
AuthorDate: Wed Feb 7 17:48:03 2024 +0800

    feat: add redis and redis-cluster in limit-conn (#10866)
---
 apisix/cli/ngx_tpl.lua                             |   1 +
 apisix/plugins/limit-conn.lua                      |  36 +-
 apisix/plugins/limit-conn/init.lua                 |  33 +-
 .../limit-conn/limit-conn-redis-cluster.lua        |  78 ++
 apisix/plugins/limit-conn/limit-conn-redis.lua     |  85 +++
 apisix/plugins/limit-conn/util.lua                 |  81 +++
 apisix/plugins/limit-count/init.lua                |  60 +-
 .../limit-count/limit-count-redis-cluster.lua      |  38 +-
 apisix/plugins/limit-count/limit-count-redis.lua   |  48 +-
 apisix/utils/redis-schema.lua                      |  81 +++
 .../limit-count-redis.lua => utils/redis.lua}      |  73 +-
 .../rediscluster.lua}                              |  73 +-
 conf/config-default.yaml                           |   1 +
 docs/en/latest/plugins/limit-conn.md               |  35 +-
 docs/zh/latest/plugins/limit-conn.md               |  35 +-
 t/APISIX.pm                                        |   1 +
 t/plugin/limit-conn-redis-cluster.t                | 339 +++++++++
 t/plugin/limit-conn-redis.t                        | 810 +++++++++++++++++++++
 18 files changed, 1607 insertions(+), 301 deletions(-)

diff --git a/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua
index 532debe0a..f1b10499c 100644
--- a/apisix/cli/ngx_tpl.lua
+++ b/apisix/cli/ngx_tpl.lua
@@ -289,6 +289,7 @@ http {
 
     {% if enabled_plugins["limit-conn"] then %}
     lua_shared_dict plugin-limit-conn {* 
http.lua_shared_dict["plugin-limit-conn"] *};
+    lua_shared_dict plugin-limit-conn-redis-cluster-slot-lock {* 
http.lua_shared_dict["plugin-limit-conn-redis-cluster-slot-lock"] *};
     {% end %}
 
     {% if enabled_plugins["limit-req"] then %}
diff --git a/apisix/plugins/limit-conn.lua b/apisix/plugins/limit-conn.lua
index d8389b701..31a29199b 100644
--- a/apisix/plugins/limit-conn.lua
+++ b/apisix/plugins/limit-conn.lua
@@ -14,15 +14,18 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-local core = require("apisix.core")
-local limit_conn = require("apisix.plugins.limit-conn.init")
+local core                              = require("apisix.core")
+local limit_conn                        = 
require("apisix.plugins.limit-conn.init")
+local redis_schema                      = require("apisix.utils.redis-schema")
+local policy_to_additional_properties   = redis_schema.schema
+local plugin_name                       = "limit-conn"
+
 
 
-local plugin_name = "limit-conn"
 local schema = {
     type = "object",
     properties = {
-        conn = {type = "integer", exclusiveMinimum = 0},
+        conn = {type = "integer", exclusiveMinimum = 0},               -- 
limit.conn max
         burst = {type = "integer",  minimum = 0},
         default_conn_delay = {type = "number", exclusiveMinimum = 0},
         only_use_default_delay = {type = "boolean", default = false},
@@ -31,6 +34,11 @@ local schema = {
             enum = {"var", "var_combination"},
             default = "var",
         },
+        policy = {
+            type = "string",
+            enum = {"redis", "redis-cluster", "local"},
+            default = "local",
+        },
         rejected_code = {
             type = "integer", minimum = 200, maximum = 599, default = 503
         },
@@ -39,7 +47,25 @@ local schema = {
         },
         allow_degradation = {type = "boolean", default = false}
     },
-    required = {"conn", "burst", "default_conn_delay", "key"}
+    required = {"conn", "burst", "default_conn_delay", "key"},
+    ["if"] = {
+        properties = {
+            policy = {
+                enum = {"redis"},
+            },
+        },
+    },
+    ["then"] = policy_to_additional_properties.redis,
+    ["else"] = {
+        ["if"] = {
+            properties = {
+                policy = {
+                    enum = {"redis-cluster"},
+                },
+            },
+        },
+        ["then"] = policy_to_additional_properties["redis-cluster"],
+    }
 }
 
 local _M = {
diff --git a/apisix/plugins/limit-conn/init.lua 
b/apisix/plugins/limit-conn/init.lua
index c6ce55f24..d7401df9c 100644
--- a/apisix/plugins/limit-conn/init.lua
+++ b/apisix/plugins/limit-conn/init.lua
@@ -23,6 +23,16 @@ if ngx.config.subsystem == "stream" then
     shdict_name = shdict_name .. "-stream"
 end
 
+local redis_single_new
+local redis_cluster_new
+do
+    local redis_src = "apisix.plugins.limit-conn.limit-conn-redis"
+    redis_single_new = require(redis_src).new
+
+    local cluster_src = "apisix.plugins.limit-conn.limit-conn-redis-cluster"
+    redis_cluster_new = require(cluster_src).new
+end
+
 
 local lrucache = core.lrucache.new({
     type = "plugin",
@@ -31,9 +41,26 @@ local _M = {}
 
 
 local function create_limit_obj(conf)
-    core.log.info("create new limit-conn plugin instance")
-    return limit_conn_new(shdict_name, conf.conn, conf.burst,
-                          conf.default_conn_delay)
+    if conf.policy == "local" then
+        core.log.info("create new limit-conn plugin instance")
+        return limit_conn_new(shdict_name, conf.conn, conf.burst,
+                              conf.default_conn_delay)
+    elseif conf.policy == "redis" then
+
+        core.log.info("create new limit-conn redis plugin instance")
+
+        return redis_single_new("plugin-limit-conn", conf, conf.conn, 
conf.burst,
+                                conf.default_conn_delay)
+
+    elseif conf.policy == "redis-cluster" then
+
+        core.log.info("create new limit-conn redis-cluster plugin instance")
+
+        return redis_cluster_new("plugin-limit-conn", conf, conf.conn, 
conf.burst,
+                                 conf.default_conn_delay)
+    else
+        return nil, "policy enum not match"
+    end
 end
 
 
diff --git a/apisix/plugins/limit-conn/limit-conn-redis-cluster.lua 
b/apisix/plugins/limit-conn/limit-conn-redis-cluster.lua
new file mode 100644
index 000000000..9e46a04b2
--- /dev/null
+++ b/apisix/plugins/limit-conn/limit-conn-redis-cluster.lua
@@ -0,0 +1,78 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local redis_cluster     = require("apisix.utils.rediscluster")
+local core              = require("apisix.core")
+local util              = require("apisix.plugins.limit-conn.util")
+local setmetatable      = setmetatable
+local ngx_timer_at      = ngx.timer.at
+
+local _M = {version = 0.1}
+
+
+local mt = {
+    __index = _M
+}
+
+
+function _M.new(plugin_name, conf, max, burst, default_conn_delay)
+
+    local red_cli, err = redis_cluster.new(conf, 
"plugin-limit-conn-redis-cluster-slot-lock")
+    if not red_cli then
+        return nil, err
+    end
+    local self = {
+        conf = conf,
+        plugin_name = plugin_name,
+        burst = burst,
+        max = max + 0,    -- just to ensure the param is good
+        unit_delay = default_conn_delay,
+        red_cli = red_cli,
+    }
+    return setmetatable(self, mt)
+end
+
+
+function _M.incoming(self, key, commit)
+    return util.incoming(self, self.red_cli, key, commit)
+end
+
+
+function _M.is_committed(self)
+    return self.committed
+end
+
+
+local function leaving_thread(premature, self, key, req_latency)
+    return util.leaving(self, self.red_cli, key, req_latency)
+end
+
+
+function _M.leaving(self, key, req_latency)
+    -- log_by_lua can't use cosocket
+    local ok, err = ngx_timer_at(0, leaving_thread, self, key, req_latency)
+    if not ok then
+        core.log.error("failed to create timer: ", err)
+        return nil, err
+    end
+
+    return ok
+
+end
+
+
+
+return _M
diff --git a/apisix/plugins/limit-conn/limit-conn-redis.lua 
b/apisix/plugins/limit-conn/limit-conn-redis.lua
new file mode 100644
index 000000000..4de7a27fd
--- /dev/null
+++ b/apisix/plugins/limit-conn/limit-conn-redis.lua
@@ -0,0 +1,85 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local redis         = require("apisix.utils.redis")
+local core          = require("apisix.core")
+local util          = require("apisix.plugins.limit-conn.util")
+local ngx_timer_at  = ngx.timer.at
+
+local setmetatable  = setmetatable
+
+
+local _M = {version = 0.1}
+
+
+local mt = {
+    __index = _M
+}
+
+function _M.new(plugin_name, conf, max, burst, default_conn_delay)
+
+    local self = {
+        conf = conf,
+        plugin_name = plugin_name,
+        burst = burst,
+        max = max + 0,    -- just to ensure the param is good
+        unit_delay = default_conn_delay,
+    }
+    return setmetatable(self, mt)
+end
+
+
+function _M.incoming(self, key, commit)
+    local conf = self.conf
+    local red, err = redis.new(conf)
+    if not red then
+        return red, err
+    end
+    return util.incoming(self, red, key, commit)
+end
+
+
+function _M.is_committed(self)
+    return self.committed
+end
+
+
+local function leaving_thread(premature, self, key, req_latency)
+
+    local conf = self.conf
+    local red, err = redis.new(conf)
+    if not red then
+        return red, err
+    end
+    return util.leaving(self, red, key, req_latency)
+end
+
+
+function _M.leaving(self, key, req_latency)
+    -- log_by_lua can't use cosocket
+    local ok, err = ngx_timer_at(0, leaving_thread, self, key, req_latency)
+    if not ok then
+        core.log.error("failed to create timer: ", err)
+        return nil, err
+    end
+
+    return ok
+
+end
+
+
+
+return _M
diff --git a/apisix/plugins/limit-conn/util.lua 
b/apisix/plugins/limit-conn/util.lua
new file mode 100644
index 000000000..f3ba5bd76
--- /dev/null
+++ b/apisix/plugins/limit-conn/util.lua
@@ -0,0 +1,81 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+local assert            = assert
+local math              = require "math"
+local floor             = math.floor
+local _M = {version = 0.3}
+
+
+function _M.incoming(self, red, key, commit)
+    local max = self.max
+    self.committed = false
+    key = "limit_conn" .. ":" .. key
+
+    local conn, err
+    if commit then
+        conn, err = red:incrby(key, 1)
+        if not conn then
+            return nil, err
+        end
+
+        if conn > max + self.burst then
+            conn, err = red:incrby(key, -1)
+            if not conn then
+                return nil, err
+            end
+            return nil, "rejected"
+        end
+        self.committed = true
+
+    else
+        local conn_from_red, err = red:get(key)
+        if err then
+            return nil, err
+        end
+        conn = (conn_from_red or 0) + 1
+    end
+
+    if conn > max then
+        -- make the excessive connections wait
+        return self.unit_delay * floor((conn - 1) / max), conn
+    end
+
+    -- we return a 0 delay by default
+    return 0, conn
+end
+
+
+function _M.leaving(self, red, key, req_latency)
+    assert(key)
+    key = "limit_conn" .. ":" .. key
+
+    local conn, err = red:incrby(key, -1)
+    if not conn then
+        return nil, err
+    end
+
+    if req_latency then
+        local unit_delay = self.unit_delay
+        self.unit_delay = (req_latency + unit_delay) / 2
+    end
+
+    return conn
+end
+
+
+return _M
diff --git a/apisix/plugins/limit-count/init.lua 
b/apisix/plugins/limit-count/init.lua
index 049e238e9..e7d03028e 100644
--- a/apisix/plugins/limit-count/init.lua
+++ b/apisix/plugins/limit-count/init.lua
@@ -19,6 +19,8 @@ local apisix_plugin = require("apisix.plugin")
 local tab_insert = table.insert
 local ipairs = ipairs
 local pairs = pairs
+local redis_schema = require("apisix.utils.redis-schema")
+local policy_to_additional_properties = redis_schema.schema
 
 local limit_redis_cluster_new
 local limit_redis_new
@@ -40,64 +42,6 @@ local group_conf_lru = core.lrucache.new({
     type = 'plugin',
 })
 
-local policy_to_additional_properties = {
-    redis = {
-        properties = {
-            redis_host = {
-                type = "string", minLength = 2
-            },
-            redis_port = {
-                type = "integer", minimum = 1, default = 6379,
-            },
-            redis_username = {
-                type = "string", minLength = 1,
-            },
-            redis_password = {
-                type = "string", minLength = 0,
-            },
-            redis_database = {
-                type = "integer", minimum = 0, default = 0,
-            },
-            redis_timeout = {
-                type = "integer", minimum = 1, default = 1000,
-            },
-            redis_ssl = {
-                type = "boolean", default = false,
-            },
-            redis_ssl_verify = {
-                type = "boolean", default = false,
-            },
-        },
-        required = {"redis_host"},
-    },
-    ["redis-cluster"] = {
-        properties = {
-            redis_cluster_nodes = {
-                type = "array",
-                minItems = 1,
-                items = {
-                    type = "string", minLength = 2, maxLength = 100
-                },
-            },
-            redis_password = {
-                type = "string", minLength = 0,
-            },
-            redis_timeout = {
-                type = "integer", minimum = 1, default = 1000,
-            },
-            redis_cluster_name = {
-                type = "string",
-            },
-            redis_cluster_ssl = {
-                type = "boolean", default = false,
-            },
-            redis_cluster_ssl_verify = {
-                type = "boolean", default = false,
-            },
-        },
-        required = {"redis_cluster_nodes", "redis_cluster_name"},
-    },
-}
 local schema = {
     type = "object",
     properties = {
diff --git a/apisix/plugins/limit-count/limit-count-redis-cluster.lua 
b/apisix/plugins/limit-count/limit-count-redis-cluster.lua
index 7800f1c88..be7029b66 100644
--- a/apisix/plugins/limit-count/limit-count-redis-cluster.lua
+++ b/apisix/plugins/limit-count/limit-count-redis-cluster.lua
@@ -15,11 +15,10 @@
 -- limitations under the License.
 --
 
-local rediscluster = require("resty.rediscluster")
+local redis_cluster = require("apisix.utils.rediscluster")
 local core = require("apisix.core")
 local setmetatable = setmetatable
 local tostring = tostring
-local ipairs = ipairs
 
 local _M = {}
 
@@ -40,41 +39,8 @@ local script = core.string.compress_script([=[
 ]=])
 
 
-local function new_redis_cluster(conf)
-    local config = {
-        -- can set different name for different redis cluster
-        name = conf.redis_cluster_name,
-        serv_list = {},
-        read_timeout = conf.redis_timeout,
-        auth = conf.redis_password,
-        dict_name = "plugin-limit-count-redis-cluster-slot-lock",
-        connect_opts = {
-            ssl = conf.redis_cluster_ssl,
-            ssl_verify = conf.redis_cluster_ssl_verify,
-        }
-    }
-
-    for i, conf_item in ipairs(conf.redis_cluster_nodes) do
-        local host, port, err = core.utils.parse_addr(conf_item)
-        if err then
-            return nil, "failed to parse address: " .. conf_item
-                        .. " err: " .. err
-        end
-
-        config.serv_list[i] = {ip = host, port = port}
-    end
-
-    local red_cli, err = rediscluster:new(config)
-    if not red_cli then
-        return nil, "failed to new redis cluster: " .. err
-    end
-
-    return red_cli
-end
-
-
 function _M.new(plugin_name, limit, window, conf)
-    local red_cli, err = new_redis_cluster(conf)
+    local red_cli, err = redis_cluster.new(conf, 
"plugin-limit-count-redis-cluster-slot-lock")
     if not red_cli then
         return nil, err
     end
diff --git a/apisix/plugins/limit-count/limit-count-redis.lua 
b/apisix/plugins/limit-count/limit-count-redis.lua
index 85f769cbd..c40ed437f 100644
--- a/apisix/plugins/limit-count/limit-count-redis.lua
+++ b/apisix/plugins/limit-count/limit-count-redis.lua
@@ -14,7 +14,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-local redis_new = require("resty.redis").new
+local redis     = require("apisix.utils.redis")
 local core = require("apisix.core")
 local assert = assert
 local setmetatable = setmetatable
@@ -39,50 +39,6 @@ local script = core.string.compress_script([=[
     return {redis.call('incrby', KEYS[1], 0 - ARGV[3]), ttl}
 ]=])
 
-local function redis_cli(conf)
-    local red = redis_new()
-    local timeout = conf.redis_timeout or 1000    -- 1sec
-
-    red:set_timeouts(timeout, timeout, timeout)
-
-    local sock_opts = {
-        ssl = conf.redis_ssl,
-        ssl_verify = conf.redis_ssl_verify
-    }
-
-    local ok, err = red:connect(conf.redis_host, conf.redis_port or 6379, 
sock_opts)
-    if not ok then
-        return false, err
-    end
-
-    local count
-    count, err = red:get_reused_times()
-    if 0 == count then
-        if conf.redis_password and conf.redis_password ~= '' then
-            local ok, err
-            if conf.redis_username then
-                ok, err = red:auth(conf.redis_username, conf.redis_password)
-            else
-                ok, err = red:auth(conf.redis_password)
-            end
-            if not ok then
-                return nil, err
-            end
-        end
-
-        -- select db
-        if conf.redis_database ~= 0 then
-            local ok, err = red:select(conf.redis_database)
-            if not ok then
-                return false, "failed to change redis db, err: " .. err
-            end
-        end
-    elseif err then
-        -- core.log.info(" err: ", err)
-        return nil, err
-    end
-    return red, nil
-end
 
 function _M.new(plugin_name, limit, window, conf)
     assert(limit > 0 and window > 0)
@@ -98,7 +54,7 @@ end
 
 function _M.incoming(self, key, cost)
     local conf = self.conf
-    local red, err = redis_cli(conf)
+    local red, err = redis.new(conf)
     if not red then
         return red, err, 0
     end
diff --git a/apisix/utils/redis-schema.lua b/apisix/utils/redis-schema.lua
new file mode 100644
index 000000000..c9fdec41d
--- /dev/null
+++ b/apisix/utils/redis-schema.lua
@@ -0,0 +1,81 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+local policy_to_additional_properties = {
+    redis = {
+        properties = {
+            redis_host = {
+                type = "string", minLength = 2
+            },
+            redis_port = {
+                type = "integer", minimum = 1, default = 6379,
+            },
+            redis_username = {
+                type = "string", minLength = 1,
+            },
+            redis_password = {
+                type = "string", minLength = 0,
+            },
+            redis_database = {
+                type = "integer", minimum = 0, default = 0,
+            },
+            redis_timeout = {
+                type = "integer", minimum = 1, default = 1000,
+            },
+            redis_ssl = {
+                type = "boolean", default = false,
+            },
+            redis_ssl_verify = {
+                type = "boolean", default = false,
+            },
+        },
+        required = {"redis_host"},
+    },
+    ["redis-cluster"] = {
+        properties = {
+            redis_cluster_nodes = {
+                type = "array",
+                minItems = 1,
+                items = {
+                    type = "string", minLength = 2, maxLength = 100
+                },
+            },
+            redis_password = {
+                type = "string", minLength = 0,
+            },
+            redis_timeout = {
+                type = "integer", minimum = 1, default = 1000,
+            },
+            redis_cluster_name = {
+                type = "string",
+            },
+            redis_cluster_ssl = {
+                type = "boolean", default = false,
+            },
+            redis_cluster_ssl_verify = {
+                type = "boolean", default = false,
+            },
+        },
+        required = {"redis_cluster_nodes", "redis_cluster_name"},
+    },
+}
+
+local _M = {
+    schema = policy_to_additional_properties
+}
+
+return _M
diff --git a/apisix/plugins/limit-count/limit-count-redis.lua 
b/apisix/utils/redis.lua
similarity index 56%
copy from apisix/plugins/limit-count/limit-count-redis.lua
copy to apisix/utils/redis.lua
index 85f769cbd..423ad6d24 100644
--- a/apisix/plugins/limit-count/limit-count-redis.lua
+++ b/apisix/utils/redis.lua
@@ -14,34 +14,15 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-local redis_new = require("resty.redis").new
-local core = require("apisix.core")
-local assert = assert
-local setmetatable = setmetatable
-local tostring = tostring
+local redis_new     = require("resty.redis").new
+local core          = require("apisix.core")
 
 
-local _M = {version = 0.3}
-
-
-local mt = {
-    __index = _M
-}
-
-
-local script = core.string.compress_script([=[
-    assert(tonumber(ARGV[3]) >= 1, "cost must be at least 1")
-    local ttl = redis.call('ttl', KEYS[1])
-    if ttl < 0 then
-        redis.call('set', KEYS[1], ARGV[1] - ARGV[3], 'EX', ARGV[2])
-        return {ARGV[1] - ARGV[3], ARGV[2]}
-    end
-    return {redis.call('incrby', KEYS[1], 0 - ARGV[3]), ttl}
-]=])
+local _M = {version = 0.1}
 
 local function redis_cli(conf)
     local red = redis_new()
-    local timeout = conf.redis_timeout or 1000    -- 1sec
+    local timeout = conf.redis_timeout or 1000    -- default 1sec
 
     red:set_timeouts(timeout, timeout, timeout)
 
@@ -52,6 +33,7 @@ local function redis_cli(conf)
 
     local ok, err = red:connect(conf.redis_host, conf.redis_port or 6379, 
sock_opts)
     if not ok then
+        core.log.error(" redis connect error, error: ", err)
         return false, err
     end
 
@@ -84,50 +66,9 @@ local function redis_cli(conf)
     return red, nil
 end
 
-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,
-    }
-    return setmetatable(self, mt)
-end
-
-function _M.incoming(self, key, cost)
-    local conf = self.conf
-    local red, err = redis_cli(conf)
-    if not red then
-        return red, err, 0
-    end
-
-    local limit = self.limit
-    local window = self.window
-    local res
-    key = self.plugin_name .. tostring(key)
-
-    local ttl = 0
-    res, err = red:eval(script, 1, key, limit, window, cost or 1)
-
-    if err then
-        return nil, err, ttl
-    end
-
-    local remaining = res[1]
-    ttl = res[2]
 
-    local ok, err = red:set_keepalive(10000, 100)
-    if not ok then
-        return nil, err, ttl
-    end
-
-    if remaining < 0 then
-        return nil, "rejected", ttl
-    end
-    return 0, remaining, ttl
+function _M.new(conf)
+    return redis_cli(conf)
 end
 
-
 return _M
diff --git a/apisix/plugins/limit-count/limit-count-redis-cluster.lua 
b/apisix/utils/rediscluster.lua
similarity index 50%
copy from apisix/plugins/limit-count/limit-count-redis-cluster.lua
copy to apisix/utils/rediscluster.lua
index 7800f1c88..e3bda4aa3 100644
--- a/apisix/plugins/limit-count/limit-count-redis-cluster.lua
+++ b/apisix/utils/rediscluster.lua
@@ -14,40 +14,19 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
+local rediscluster      = require("resty.rediscluster")
+local core              = require("apisix.core")
+local ipairs            = ipairs
 
-local rediscluster = require("resty.rediscluster")
-local core = require("apisix.core")
-local setmetatable = setmetatable
-local tostring = tostring
-local ipairs = ipairs
+local _M = {version = 0.1}
 
-local _M = {}
-
-
-local mt = {
-    __index = _M
-}
-
-
-local script = core.string.compress_script([=[
-    assert(tonumber(ARGV[3]) >= 1, "cost must be at least 1")
-    local ttl = redis.call('ttl', KEYS[1])
-    if ttl < 0 then
-        redis.call('set', KEYS[1], ARGV[1] - ARGV[3], 'EX', ARGV[2])
-        return {ARGV[1] - ARGV[3], ARGV[2]}
-    end
-    return {redis.call('incrby', KEYS[1], 0 - ARGV[3]), ttl}
-]=])
-
-
-local function new_redis_cluster(conf)
+local function new_redis_cluster(conf, dict_name)
     local config = {
-        -- can set different name for different redis cluster
         name = conf.redis_cluster_name,
         serv_list = {},
         read_timeout = conf.redis_timeout,
         auth = conf.redis_password,
-        dict_name = "plugin-limit-count-redis-cluster-slot-lock",
+        dict_name = dict_name,
         connect_opts = {
             ssl = conf.redis_cluster_ssl,
             ssl_verify = conf.redis_cluster_ssl_verify,
@@ -73,44 +52,8 @@ local function new_redis_cluster(conf)
 end
 
 
-function _M.new(plugin_name, limit, window, conf)
-    local red_cli, err = new_redis_cluster(conf)
-    if not red_cli then
-        return nil, err
-    end
-
-    local self = {
-        limit = limit,
-        window = window,
-        conf = conf,
-        plugin_name = plugin_name,
-        red_cli = red_cli,
-    }
-
-    return setmetatable(self, mt)
-end
-
-
-function _M.incoming(self, key, cost)
-    local red = self.red_cli
-    local limit = self.limit
-    local window = self.window
-    key = self.plugin_name .. tostring(key)
-
-    local ttl = 0
-    local res, err = red:eval(script, 1, key, limit, window, cost or 1)
-
-    if err then
-        return nil, err, ttl
-    end
-
-    local remaining = res[1]
-    ttl = res[2]
-
-    if remaining < 0 then
-        return nil, "rejected", ttl
-    end
-    return 0, remaining, ttl
+function _M.new(conf, dict_name)
+     return new_redis_cluster(conf, dict_name)
 end
 
 
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index fcee30871..3ba8a4e46 100755
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -260,6 +260,7 @@ nginx_config:                     # Config for render the 
template to generate n
       balancer-ewma-locks: 10m
       balancer-ewma-last-touched-at: 10m
       plugin-limit-count-redis-cluster-slot-lock: 1m
+      plugin-limit-conn-redis-cluster-slot-lock: 1m
       tracing_buffer: 10m
       plugin-api-breaker: 10m
       etcd-cluster-health-check: 10m
diff --git a/docs/en/latest/plugins/limit-conn.md 
b/docs/en/latest/plugins/limit-conn.md
index af0e59bc0..f95995530 100644
--- a/docs/en/latest/plugins/limit-conn.md
+++ b/docs/en/latest/plugins/limit-conn.md
@@ -32,17 +32,30 @@ The `limit-conn` Plugin limits the number of concurrent 
requests to your service
 
 ## Attributes
 
-| Name                   | Type    | Required | Default | Valid values         
      | Description                                                             
                                                                                
                                                                                
                                                                                
                                                                              |
-| ---------------------- | ------- | -------- | ------- | 
-------------------------- | 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 |
-| conn                   | integer | True     |         | conn > 0             
      | Maximum number of concurrent requests allowed. Requests exceeding this 
ratio (and below `conn` + `burst`) will be delayed (configured by 
`default_conn_delay`).                                                          
                                                                                
                                                                                
             |
-| burst                  | integer | True     |         | burst >= 0           
      | Number of additional concurrent requests allowed to be delayed per 
second. If the number exceeds this hard limit, they will get rejected 
immediately.                                                                    
                                                                                
                                                                                
             |
-| default_conn_delay     | number  | True     |         | default_conn_delay > 
0     | Delay in seconds to process the concurrent requests exceeding `conn` 
(and `conn` + `burst`).                                                         
                                                                                
                                                                                
                                                                                
 |
-| only_use_default_delay | boolean | False    | false   | [true,false]         
      | When set to `true`, the Plugin will always set a delay of 
`default_conn_delay` and would not use any other calculations.                  
                                                                                
                                                                                
                                                                                
            |
-| key_type               | string  | False    | "var"   | ["var", 
"var_combination"] | Type of user specified key to use.                         
                                                                                
                                                                                
                                                                                
                                                                                
           |
-| key                    | string  | True     |         |                      
      | User specified key to base the request limiting on. If the `key_type` 
attribute is set to `"var"`, the key will be treated as a name of variable, 
like `remote_addr` or `consumer_name`. If the `key_type` is set to 
`"var_combination"`, the key will be a combination of variables, like 
`$remote_addr $consumer_name`. If the value of the key is empty, `remote_addr` 
will be set as the default key. |
-| rejected_code          | string  | False    | 503     | [200,...,599]        
      | HTTP status code returned when the requests exceeding the threshold are 
rejected.                                                                       
                                                                                
                                                                                
                                                                              |
-| rejected_msg           | string  | False    |         | non-empty            
      | Body of the response returned when the requests exceeding the threshold 
are rejected.                                                                   
                                                                                
                                                                                
                                                                              |
-| allow_degradation      | boolean | False    | false   |                      
      | When set to `true` enables Plugin degradation when the Plugin is 
temporarily unavailable and allows requests to continue.                        
                                                                                
                                                                                
                                                                                
     |
+| Name                     | Type    | Required | Default     | Valid values   
                   | Description                                                
                                                                                
                                                                                
                                                                                
                                                                                
               |
+|--------------------------|---------| -------- 
|-------------|-----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| conn                     | integer | True     |             | conn > 0       
                   | Maximum number of concurrent requests allowed. Requests 
exceeding this ratio (and below `conn` + `burst`) will be delayed (configured 
by `default_conn_delay`).                                                       
                                                                                
                                                                                
                    |
+| burst                    | integer | True     |             | burst >= 0     
                   | Number of additional concurrent requests allowed to be 
delayed per second. If the number exceeds this hard limit, they will get 
rejected immediately.                                                           
                                                                                
                                                                                
                          |
+| default_conn_delay       | number  | True     |             | 
default_conn_delay > 0            | Delay in seconds to process the concurrent 
requests exceeding `conn` (and `conn` + `burst`).                               
                                                                                
                                                                                
                                                                                
                               |
+| only_use_default_delay   | boolean | False    | false       | [true,false]   
                   | When set to `true`, the Plugin will always set a delay of 
`default_conn_delay` and would not use any other calculations.                  
                                                                                
                                                                                
                                                                                
                |
+| key_type                 | string  | False    | "var"       | ["var", 
"var_combination"]        | Type of user specified key to use.                  
                                                                                
                                                                                
                                                                                
                                                                                
                      |
+| key                      | string  | True     |             |                
                   | User specified key to base the request limiting on. If the 
`key_type` attribute is set to `"var"`, the key will be treated as a name of 
variable, like `remote_addr` or `consumer_name`. If the `key_type` is set to 
`"var_combination"`, the key will be a combination of variables, like 
`$remote_addr $consumer_name`. If the value of the key is empty, `remote_addr` 
will be set as the default key. |
+| rejected_code            | string  | False    | 503         | [200,...,599]  
                   | HTTP status code returned when the requests exceeding the 
threshold are rejected.                                                         
                                                                                
                                                                                
                                                                                
                |
+| rejected_msg             | string  | False    |             | non-empty      
                   | Body of the response returned when the requests exceeding 
the threshold are rejected.                                                     
                                                                                
                                                                                
                                                                                
                |
+| allow_degradation        | boolean | False    | false       |                
                   | When set to `true` enables Plugin degradation when the 
Plugin is temporarily unavailable and allows requests to continue.              
                                                                                
                                                                                
                                                                                
                   |
+| policy                  | string  | False                                    
 | "local"       | ["local", "redis", "redis-cluster"]    | Rate-limiting 
policies to use for retrieving and increment the limit count. When set to 
`local` the counters will be locally stored in memory on the node. When set to 
`redis` counters are stored on a Redis server and will be shared across the 
nodes. It is done usually for global speed limiting, and setting to 
`redis-cluster` uses a Redis cluster inste [...]
+| redis_host               | string  | required when `policy` is `redis`       
  |             |                                   | Address of the Redis 
server. Used when the `policy` attribute is set to `redis`.                     
                                                                                
                                                                                
                                                                                
                   [...]
+| redis_port               | integer | False                                   
  | 6379        | [1,...]                           | Port of the Redis server. 
Used when the `policy` attribute is set to `redis`.                             
                                                                                
                                                                                
                                                                                
              [...]
+| redis_username           | string  | False                                   
  |             |                                   | Username for Redis 
authentication if Redis ACL is used (for Redis version >= 6.0). If you use the 
legacy authentication method `requirepass` to configure Redis password, 
configure only the `redis_password`. Used when the `policy` is set to `redis`.  
                                                                                
                              [...]
+| redis_password           | string  | False                                   
  |             |                                   | Password for Redis 
authentication. Used when the `policy` is set to `redis` or `redis-cluster`.    
                                                                                
                                                                                
                                                                                
                     [...]
+| redis_ssl                | boolean | False                                   
  | false       |                                   | If set to `true`, then 
uses SSL to connect to redis instance. Used when the `policy` attribute is set 
to `redis`.                                                                     
                                                                                
                                                                                
                  [...]
+| redis_ssl_verify         | boolean | False                                   
  | false       |                                   | If set to `true`, then 
verifies the validity of the server SSL certificate. Used when the `policy` 
attribute is set to `redis`. See 
[tcpsock:sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake).
                                                                                
                                                          [...]
+| redis_database           | integer | False                                   
  | 0           | redis_database >= 0               | Selected database of the 
Redis server (for single instance operation or when using Redis cloud with a 
single entrypoint). Used when the `policy` attribute is set to `redis`.         
                                                                                
                                                                                
                  [...]
+| redis_timeout            | integer | False                                   
  | 1000        | [1,...]                           | Timeout in milliseconds 
for any command submitted to the Redis server. Used when the `policy` attribute 
is set to `redis` or `redis-cluster`.                                           
                                                                                
                                                                                
                [...]
+| redis_cluster_nodes      | array   | required when `policy` is 
`redis-cluster` |             |                                   | Addresses 
of Redis cluster nodes. Used when the `policy` attribute is set to 
`redis-cluster`.                                                                
                                                                                
                                                                                
                                           [...]
+| redis_cluster_name       | string  | required when `policy` is 
`redis-cluster` |             |                                   | Name of the 
Redis cluster service nodes. Used when the `policy` attribute is set to 
`redis-cluster`.                                                                
                                                                                
                                                                                
                                    [...]
+| redis_cluster_ssl        | boolean |  False | false       |                  
                 | If set to `true`, then uses SSL to connect to redis-cluster. 
Used when the `policy` attribute is set to `redis-cluster`.                     
                                                                                
                                                                                
                                                                                
       |
+| redis_cluster_ssl_verify | boolean | False | false       |                   
                | If set to `true`, then verifies the validity of the server 
SSL certificate. Used when the `policy` attribute is set to `redis-cluster`.    
                                                                                
                                                                                
                                                                                
         |
 
 ## Enable Plugin
 
diff --git a/docs/zh/latest/plugins/limit-conn.md 
b/docs/zh/latest/plugins/limit-conn.md
index 794835e42..f91df1f5a 100644
--- a/docs/zh/latest/plugins/limit-conn.md
+++ b/docs/zh/latest/plugins/limit-conn.md
@@ -32,17 +32,30 @@ description: 本文介绍了 Apache APISIX limit-conn 插件的相关操作,
 
 ## 属性
 
-| 名称               | 类型    | 必选项 | 默认值 | 有效值                      | 描述         
                                                                                
                                                                                
                              |
-| ------------------ | ------- | ----- | ------ | -------------------------- 
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| conn               | integer | 是    |        | conn > 0                   | 
允许的最大并发请求数。超过 `conn` 的限制、但是低于 `conn` + `burst` 的请求,将被延迟处理。                      
                                                                                
                 |
-| burst              | integer | 是    |        | burst >= 0                 | 
每秒允许被延迟处理的额外并发请求数。                                                              
                                                                                
                            |
-| default_conn_delay | number  | 是    |        | default_conn_delay > 0     | 
默认的典型连接(或请求)的处理延迟时间。                                                            
                                                                                
                          |
-| only_use_default_delay | boolean | 否 | false | [true,false]               | 
延迟时间的严格模式。当设置为 `true` 时,将会严格按照设置的 `default_conn_delay` 时间来进行延迟处理。               
                                                                                
               |
-| 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`;如果 `key` 的值为空,`$remote_addr` 会被作为默认 `key`。 |
-| rejected_code      | string  | 否    | 503    | [200,...,599]              | 
当请求数超过 `conn` + `burst` 阈值时,返回的 HTTP 状态码。                                       
                                                                                
                                |
-| rejected_msg       | string | 否     |        | 非空                       | 
当请求数超过 `conn` + `burst` 阈值时,返回的信息。                                              
                                                                                
                                 |
-| allow_degradation  | boolean | 否    | false  |                            | 
当设置为 `true` 时,启用插件降级并自动允许请求继续。                                                  
                                                                                
                            |
+| 名称               | 类型    | 必选项                              | 默认值 | 有效值      
                | 描述                                                            
                                                                                
                                                                        |
+| ------------------ | ------- |----------------------------------| ------ | 
-------------------------- 
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| conn               | integer | 是                                |        | 
conn > 0                   | 允许的最大并发请求数。超过 `conn` 的限制、但是低于 `conn` + `burst` 
的请求,将被延迟处理。                                                                     
                                                                                
       |
+| burst              | integer | 是                                |        | 
burst >= 0                 | 每秒允许被延迟处理的额外并发请求数。                                 
                                                                                
                                                                                
   |
+| default_conn_delay | number  | 是                                |        | 
default_conn_delay > 0     | 默认的典型连接(或请求)的处理延迟时间。                               
                                                                                
                                                                                
   |
+| only_use_default_delay | boolean | 否                                | false 
| [true,false]               | 延迟时间的严格模式。当设置为 `true` 时,将会严格按照设置的 
`default_conn_delay` 时间来进行延迟处理。                                                 
                                                                                
                    |
+| 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`;如果 `key` 
的值为空,`$remote_addr` 会被作为默认 `key`。 |
+| rejected_code      | string  | 否                                | 503    | 
[200,...,599]              | 当请求数超过 `conn` + `burst` 阈值时,返回的 HTTP 状态码。          
                                                                                
                                                                                
   |
+| rejected_msg       | string | 否                                |        | 非空 
                      | 当请求数超过 `conn` + `burst` 阈值时,返回的信息。                      
                                                                                
                                                                              |
+| allow_degradation  | boolean | 否                                | false  |   
                         | 当设置为 `true` 时,启用插件降级并自动允许请求继续。                       
                                                                                
                                                                                
 |
+| policy              | string  | 否        | "local"        | ["local", 
"redis", "redis-cluster"]    | 用于检索和增加限制计数的策略。当设置为 `local` 
时,计数器被以内存方式保存在节点本地;当设置为 `redis` 时,计数器保存在 Redis 
服务节点上,从而可以跨节点共享结果,通常用它来完成全局限速;当设置为 `redis-cluster` 时,使用 Redis 集群而不是单个实例。|
+| redis_host          | string  | 否        |               |                   
                      | 当使用 `redis` 限速策略时,Redis 服务节点的地址。**当 `policy` 属性设置为 
`redis` 时必选。**                                                                  
                                                                             |
+| redis_port          | integer | 否        | 6379          | [1,...]           
                      | 当使用 `redis` 限速策略时,Redis 服务节点的端口。                        
                                                                                
                                                                              |
+| redis_username      | string  | 否        |               |                   
                      | 若使用 Redis ACL 进行身份验证(适用于 Redis 版本 >=6.0),则需要提供 Redis 
用户名。若使用 Redis legacy 方式 `requirepass` 进行身份验证,则只需将密码配置在 `redis_password`。当 
`policy` 设置为 `redis` 时使用。                                                       
 |
+| redis_password      | string  | 否        |               |                   
                      | 当使用 `redis`  或者 `redis-cluster`  限速策略时,Redis 服务节点的密码。   
                                                                                
                                                                              |
+| redis_ssl           | boolean | 否        | false         |                   
                      | 当使用 `redis` 限速策略时,如果设置为 true,则使用 SSL 连接到 `redis`        
                                                                                
                                                                              |
+| redis_ssl_verify    | boolean | 否        | false         |                   
                      | 当使用 `redis` 限速策略时,如果设置为 true,则验证服务器 SSL 证书的有效性,具体请参考 
[tcpsock:sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake).
                                                                       |
+| redis_database      | integer | 否        | 0             | redis_database >= 
0                     | 当使用 `redis` 限速策略时,Redis 服务节点中使用的 `database`,并且只针对非 
Redis 集群模式(单实例模式或者提供单入口的 Redis 公有云服务)生效。                                        
                                                                                
   |
+| redis_timeout       | integer | 否        | 1000          | [1,...]           
                      | 当 `policy` 设置为 `redis` 或 `redis-cluster` 时,Redis 
服务节点的超时时间(以毫秒为单位)。                                                              
                                                                               |
+| redis_cluster_nodes | array   | 否        |               |                   
                      | 当使用 `redis-cluster` 限速策略时,Redis 
集群服务节点的地址列表(至少需要两个地址)。**当 `policy` 属性设置为 `redis-cluster` 时必选。**                 
                                                                                
                |
+| redis_cluster_name  | string  | 否        |               |                   
                      | 当使用 `redis-cluster` 限速策略时,Redis 集群服务节点的名称。**当 `policy` 
设置为 `redis-cluster` 时必选。**                                                      
                                                                         |
+| redis_cluster_ssl  | boolean  | 否        |     false    |                    
                     | 当使用 `redis-cluster` 限速策略时,如果设置为 true,则使用 SSL 连接到 
`redis-cluster`                                                                 
                                                                                
     |
+| redis_cluster_ssl_verify  | boolean  | 否        |     false        |         
                                | 当使用 `redis-cluster` 限速策略时,如果设置为 true,则验证服务器 
SSL 证书的有效性                                                                      
                                                                                
          |
 
 ## 启用插件
 
diff --git a/t/APISIX.pm b/t/APISIX.pm
index 6428de1c5..73de93f34 100644
--- a/t/APISIX.pm
+++ b/t/APISIX.pm
@@ -560,6 +560,7 @@ _EOC_
     lua_shared_dict balancer-ewma-locks 1m;
     lua_shared_dict balancer-ewma-last-touched-at 1m;
     lua_shared_dict plugin-limit-count-redis-cluster-slot-lock 1m;
+    lua_shared_dict plugin-limit-conn-redis-cluster-slot-lock 1m;
     lua_shared_dict tracing_buffer 10m;    # plugin skywalking
     lua_shared_dict access-tokens 1m;    # plugin authz-keycloak
     lua_shared_dict discovery 1m;    # plugin authz-keycloak
diff --git a/t/plugin/limit-conn-redis-cluster.t 
b/t/plugin/limit-conn-redis-cluster.t
new file mode 100644
index 000000000..997a4a2a7
--- /dev/null
+++ b/t/plugin/limit-conn-redis-cluster.t
@@ -0,0 +1,339 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+BEGIN {
+    if ($ENV{TEST_NGINX_CHECK_LEAK}) {
+        $SkipReason = "unavailable for the hup tests";
+
+    } else {
+        $ENV{TEST_NGINX_USE_HUP} = 1;
+        undef $ENV{TEST_NGINX_USE_STAP};
+    }
+}
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+    my $port = $ENV{TEST_NGINX_SERVER_PORT};
+
+    my $config = $block->config // <<_EOC_;
+    location /access_root_dir {
+        content_by_lua_block {
+            local httpc = require "resty.http"
+            local hc = httpc:new()
+
+            local res, err = 
hc:request_uri('http://127.0.0.1:$port/limit_conn')
+            if res then
+                ngx.exit(res.status)
+            end
+        }
+    }
+
+    location /test_concurrency {
+        content_by_lua_block {
+            local reqs = {}
+            local status_map = {}
+            for i = 1, 10 do
+                reqs[i] = { "/access_root_dir" }
+            end
+            local resps = { ngx.location.capture_multi(reqs) }
+            for i, resp in ipairs(resps) do
+                local status_key = resp.status
+                if status_map[status_key] then
+                    status_map[status_key] = status_map[status_key] + 1
+                else
+                    status_map[status_key] = 1
+                end
+            end
+            for key, value in pairs(status_map) do
+                ngx.say("status:" .. key .. ", " .. "count:" .. value)
+            end
+        }
+    }
+_EOC_
+
+    $block->set_value("config", $config);
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.limit-conn")
+            local ok, err = plugin.check_schema({
+                conn = 1,
+                burst = 0,
+                default_conn_delay = 0.1,
+                rejected_code = 503,
+                key = 'remote_addr',
+                policy = "redis-cluster",
+                redis_cluster_nodes = {
+                    "127.0.0.1:5000",
+                    "127.0.0.1:5003",
+                    "127.0.0.1:5002"
+                },
+                dict_name = "test",
+                redis_cluster_name = "test"
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+done
+
+
+
+=== TEST 2: add plugin
+--- 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": 100,
+                                "burst": 50,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "policy": "redis-cluster",
+                                "redis_cluster_nodes": [
+                                    "127.0.0.1:5000",
+                                    "127.0.0.1:5003",
+                                    "127.0.0.1:5002"
+                                ],
+                                "redis_cluster_name": "test"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 3: not exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:10
+
+
+
+=== TEST 4: update plugin
+--- 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": "remote_addr",
+                                "policy": "redis-cluster",
+                                "redis_cluster_nodes": [
+                                    "127.0.0.1:5000",
+                                    "127.0.0.1:5002"
+                                ],
+                                "redis_cluster_name": "redis-cluster-1"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 5: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:3
+status:503, count:7
+
+
+
+=== TEST 6: update plugin
+--- 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": 5,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "policy": "redis-cluster",
+                                "redis_cluster_nodes": [
+                                    "127.0.0.1:5000",
+                                    "127.0.0.1:5002"
+                                ],
+                                "redis_cluster_name": "redis-cluster-1"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 7: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:6
+status:503, count:4
+
+
+
+=== TEST 8: set route, with redis_cluster_nodes and redis_cluster_name 
redis_cluster_ssl and redis_cluster_ssl_verify
+--- 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": 5,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "policy": "redis-cluster",
+                                "redis_cluster_nodes": [
+                                    "127.0.0.1:7001",
+                                    "127.0.0.1:7002",
+                                    "127.0.0.1:7000"
+                                ],
+                                "redis_cluster_name": "redis-cluster-2",
+                                "redis_cluster_ssl": true,
+                                "redis_cluster_ssl_verify": false
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 9: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:6
+status:503, count:4
diff --git a/t/plugin/limit-conn-redis.t b/t/plugin/limit-conn-redis.t
new file mode 100644
index 000000000..a12145370
--- /dev/null
+++ b/t/plugin/limit-conn-redis.t
@@ -0,0 +1,810 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+BEGIN {
+    if ($ENV{TEST_NGINX_CHECK_LEAK}) {
+        $SkipReason = "unavailable for the hup tests";
+
+    } else {
+        $ENV{TEST_NGINX_USE_HUP} = 1;
+        undef $ENV{TEST_NGINX_USE_STAP};
+    }
+}
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+    my $port = $ENV{TEST_NGINX_SERVER_PORT};
+
+    my $config = $block->config // <<_EOC_;
+    location /access_root_dir {
+        content_by_lua_block {
+            local httpc = require "resty.http"
+            local hc = httpc:new()
+
+            local res, err = 
hc:request_uri('http://127.0.0.1:$port/limit_conn')
+            if res then
+                ngx.exit(res.status)
+            end
+        }
+    }
+
+    location /test_concurrency {
+        content_by_lua_block {
+            local reqs = {}
+            local status_map = {}
+            for i = 1, 10 do
+                reqs[i] = { "/access_root_dir" }
+            end
+            local resps = { ngx.location.capture_multi(reqs) }
+            for i, resp in ipairs(resps) do
+                local status_key = resp.status
+                if status_map[status_key] then
+                    status_map[status_key] = status_map[status_key] + 1
+                else
+                    status_map[status_key] = 1
+                end
+            end
+            for key, value in pairs(status_map) do
+                ngx.say("status:" .. key .. ", " .. "count:" .. value)
+            end
+        }
+    }
+_EOC_
+
+    $block->set_value("config", $config);
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.limit-conn")
+            local ok, err = plugin.check_schema({
+                conn = 1,
+                burst = 0,
+                default_conn_delay = 0.1,
+                rejected_code = 503,
+                key = 'remote_addr',
+                policy = "redis",
+                redis_host = 'localhost',
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+done
+
+
+
+=== TEST 2: add plugin
+--- 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": 100,
+                                "burst": 50,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1",
+                                "redis_port": 6379
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 3: not exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:10
+
+
+
+=== TEST 4: update plugin
+--- 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": "remote_addr",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1",
+                                "redis_port": 6379
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 5: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:3
+status:503, count:7
+
+
+
+=== TEST 6: update plugin
+--- 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": 5,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1",
+                                "redis_port": 6379
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 7: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:6
+status:503, count:4
+
+
+
+=== TEST 8: update plugin with username, password
+--- 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": 5,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1",
+                                "redis_port": 6379,
+                                "redis_username": "alice",
+                                "redis_password": "somepassword"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 9: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:6
+status:503, count:4
+
+
+
+=== TEST 10: update plugin with username, wrong password
+--- 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": 5,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1",
+                                "redis_port": 6379,
+                                "redis_username": "alice",
+                                "redis_password": "someerrorpassword"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 11: catch wrong pass
+--- request
+GET /access_root_dir
+--- error_code: 500
+--- error_log
+failed to limit conn: WRONGPASS invalid username-password pair or user is 
disabled.
+
+
+
+=== TEST 12: invalid route: missing redis_host
+--- 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": {
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "remote_addr",
+                                "conn": 1,
+                                "policy": "redis"
+                            }
+                        },
+                        "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: then 
clause did not match"}
+
+
+
+=== TEST 13: disable plugin
+--- 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": {
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 14: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:10
+
+
+
+=== TEST 15: set route(key: server_addr)
+--- 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": 100,
+                                "burst": 50,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "server_addr",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 16: key: http_x_real_ip
+--- 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": 5,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "http_x_real_ip",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 17: exceeding the burst (X-Real-IP)
+--- config
+location /access_root_dir {
+    content_by_lua_block {
+        local port = ngx.var.server_port
+        local httpc = require "resty.http"
+        local hc = httpc:new()
+
+        local res, err = hc:request_uri('http://127.0.0.1:' .. port .. 
'/limit_conn', {
+            keepalive = false,
+            headers = {["X-Real-IP"] = "10.10.10.1"}
+        })
+        if res then
+            ngx.exit(res.status)
+        end
+    }
+}
+
+location /test_concurrency {
+    content_by_lua_block {
+        local reqs = {}
+        local status_map = {}
+        for i = 1, 10 do
+            reqs[i] = { "/access_root_dir" }
+        end
+        local resps = { ngx.location.capture_multi(reqs) }
+        for i, resp in ipairs(resps) do
+            local status_key = resp.status
+            if status_map[status_key] then
+                status_map[status_key] = status_map[status_key] + 1
+            else
+                status_map[status_key] = 1
+            end
+        end
+        for key, value in pairs(status_map) do
+            ngx.say("status:" .. key .. ", " .. "count:" .. value)
+        end
+    }
+}
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:6
+status:503, count:4
+--- error_log
+limit key: 10.10.10.1route
+
+
+
+=== TEST 18: key: http_x_forwarded_for
+--- 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": 5,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "rejected_code": 503,
+                                "key": "http_x_forwarded_for",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 19: exceeding the burst(X-Forwarded-For)
+--- config
+location /access_root_dir {
+    content_by_lua_block {
+        local port = ngx.var.server_port
+        local httpc = require "resty.http"
+        local hc = httpc:new()
+
+        local res, err = hc:request_uri('http://127.0.0.1:' .. port .. 
'/limit_conn', {
+            keepalive = false,
+            headers = {["X-Forwarded-For"] = "10.10.10.2"}
+        })
+        if res then
+            ngx.exit(res.status)
+        end
+    }
+}
+
+location /test_concurrency {
+    content_by_lua_block {
+        local reqs = {}
+        local status_map = {}
+        for i = 1, 10 do
+            reqs[i] = { "/access_root_dir" }
+        end
+        local resps = { ngx.location.capture_multi(reqs) }
+        for i, resp in ipairs(resps) do
+            local status_key = resp.status
+            if status_map[status_key] then
+                status_map[status_key] = status_map[status_key] + 1
+            else
+                status_map[status_key] = 1
+            end
+        end
+        for key, value in pairs(status_map) do
+            ngx.say("status:" .. key .. ", " .. "count:" .. value)
+        end
+    }
+}
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:6
+status:503, count:4
+--- error_log
+limit key: 10.10.10.2route
+
+
+
+=== TEST 20: default rejected_code
+--- 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": 4,
+                                "burst": 1,
+                                "default_conn_delay": 0.1,
+                                "key": "remote_addr",
+                                "policy": "redis",
+                                "redis_host": "127.0.0.1"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/limit_conn"
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 21: exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:5
+status:503, count:5
+
+
+
+=== TEST 22: set global rule with conn = 2
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/global_rules/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "limit-conn": {
+                            "conn": 2,
+                            "burst": 1,
+                            "default_conn_delay": 0.1,
+                            "key": "remote_addr",
+                            "policy": "redis",
+                            "redis_host": "127.0.0.1"
+                        }
+                    }
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 23: exceeding the burst of global rule
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:3
+status:503, count:7
+
+
+
+=== TEST 24: delete global rule
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/global_rules/1',
+                ngx.HTTP_DELETE
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 25: not exceeding the burst
+--- request
+GET /test_concurrency
+--- timeout: 10s
+--- response_body
+status:200, count:5
+status:503, count:5


Reply via email to