-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: limit count plugin support X-RateLimit-Reset #8578
feat: limit count plugin support X-RateLimit-Reset #8578
Conversation
…and return header when rejected
apisix/plugins/limit-count/init.lua
Outdated
@@ -38,7 +39,9 @@ local lrucache = core.lrucache.new({ | |||
local group_conf_lru = core.lrucache.new({ | |||
type = 'plugin', | |||
}) | |||
|
|||
local resetlru = core.lrucache.new({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
local resetlru = core.lrucache.new({ | |
local reset_lru = core.lrucache.new({ |
apisix/plugins/limit-count/init.lua
Outdated
} | ||
end | ||
|
||
return resetlru(key,"",create,conf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to add space around the operator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
apisix/plugins/limit-count/init.lua
Outdated
@@ -242,6 +245,15 @@ local function gen_limit_obj(conf, ctx) | |||
return core.lrucache.plugin_ctx(lrucache, ctx, extra_key, create_limit_obj, conf) | |||
end | |||
|
|||
local function get_end_time(conf,key) | |||
local create = function() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid creating new function per call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
@@ -254,7 +254,7 @@ curl -i http://127.0.0.1:9180/apisix/admin/routes/1 \ | |||
|
|||
## Example usage | |||
|
|||
The above configuration limits to 2 requests in 60 seconds. The first two requests will work and the response headers will contain the headers `X-RateLimit-Limit` and `X-RateLimit-Remaining`: | |||
The above configuration limits to 2 requests in 60 seconds. The first two requests will work and the response headers will contain the headers `X-RateLimit-Limit` and `X-RateLimit-Remaining` and `X-RateLimit-Reset`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the English version doesn't match the Chinese one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
apisix/plugins/limit-count/init.lua
Outdated
local end_time = ngx_time() + conf.time_window | ||
-- save to lrucache by key | ||
reset = conf.time_window | ||
local end_time_obj = get_end_time(conf,key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default, the client IP will be used as key, which means there will be a great number of keys per conf. The size of lrucache is limited.
There come two solutions:
- store the end time info in the actual backend like redis cluster.
2. write a new cache with dynamical expire support to store the end time info, but this way is not memory-effective.(it doesn't work)
Personally, I prefer solution 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
solution 1 does not work either. Because by default limit count will not use Redis as the backend. So we can't save it to Redis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using ngx.shared
with expire time instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ngx.shared
only works with the default backend which also uses ngx.shared
. If we use other backends, only one instance will execute remaining == conf.count - 1
and the end time can only be found in that instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, you can review it again.
function _M.read_reset(self, key) | ||
local red = self.red_cli | ||
key = self.plugin_name .. tostring(key) | ||
local ttl, err = red:ttl(key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put this into the eval script to make it atomic and reduce roundtrip?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
__index = _M | ||
} | ||
|
||
function _M.set_endtime(self,key,time_window) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look like we can merge set_endtime
into the incoming
method, especially after implementing read_reset
via eval?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, fixed
apisix/cli/ngx_tpl.lua
Outdated
@@ -276,6 +276,7 @@ http { | |||
{% if enabled_plugins["limit-count"] then %} | |||
lua_shared_dict plugin-limit-count {* http.lua_shared_dict["plugin-limit-count"] *}; | |||
lua_shared_dict plugin-limit-count-redis-cluster-slot-lock {* http.lua_shared_dict["plugin-limit-count-redis-cluster-slot-lock"] *}; | |||
lua_shared_dict plugin-limit-count-reset-header {* http.lua_shared_dict["plugin-limit-count-reset-header"] *}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can let the size of plugin-limit-count-reset-header be equal to plugin-limit-count, so that people don't need to configure it twice? As these two shdict store similar data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed plugin-limit-count-reset-header
in config
return setmetatable(self, mt) | ||
end | ||
|
||
function _M.set_endtime(self,key,time_window) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to expose these methods now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, removed
@@ -91,16 +92,18 @@ function _M.incoming(self, key) | |||
local window = self.window | |||
key = self.plugin_name .. tostring(key) | |||
|
|||
local remaining, err = red:eval(script, 1, key, limit, window) | |||
local res, err = red:eval(script, 1, key, limit, window) | |||
local remaining = res[1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use the res after checking the err
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
t/cli/test_main.sh
Outdated
@@ -807,6 +807,7 @@ nginx_config: | |||
internal-status: 20m | |||
plugin-limit-req: 20m | |||
plugin-limit-count: 20m | |||
plugin-limit-count-reset-header: 20m |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need this setting, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I forget to remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I'm removed this. Pls review again
end | ||
|
||
function _M.incoming(self, key, commit, conf) | ||
local delay, remaining = self.limit_count:incoming(key, commit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
local delay, remaining = self.limit_count:incoming(key, commit) | |
local delay, remaining = self.limit_count:incoming(key, commit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
apisix/plugins/limit-count/init.lua
Outdated
@@ -302,7 +309,8 @@ function _M.rate_limit(conf, ctx) | |||
|
|||
if conf.show_limit_quota_header then | |||
core.response.set_header("X-RateLimit-Limit", conf.count, | |||
"X-RateLimit-Remaining", remaining) | |||
"X-RateLimit-Remaining", remaining, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change 4-spaces indentation to 8-spaces here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
apisix/plugins/limit-count/init.lua
Outdated
-- show count limit header when rejected | ||
if conf.show_limit_quota_header then | ||
core.response.set_header("X-RateLimit-Limit", conf.count, | ||
"X-RateLimit-Remaining", 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
-- set an end time | ||
local end_time = ngx_time() + time_window | ||
-- save to dict by key | ||
self.dict:set(key, end_time, time_window) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to check the err returned from set
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
function _M.incoming(self, key) | ||
local conf = self.conf | ||
local red, err = redis_cli(conf) | ||
if err then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err then | |
if not red then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
local conf = self.conf | ||
local red, err = redis_cli(conf) | ||
if err then | ||
return red, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return red, err | |
return nil, err, 0 |
Please follow the code style: https://github.com/apache/apisix/blob/master/CODE_STYLE.md#error-handling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
Description
limit count plugin support returns a new header
X-RateLimit-Reset
and return the header when RejectedFixes #8381
Checklist