limit_req 不同时段的不同区域
limit_req differ zones by time of day
我想根据一天中的时间限制对我的服务的请求。
晚上 (21:00 - 06:59) 应该是 4 r/s,白天 (07:00-20:59) 应该是 1 r/s。
我可以有 2 个不同的 req_limit_zone
,比如
limit_req_zone $server_name zone=day:1m rate=1r/s;
limit_req_zone $server_name zone=night:1m rate=4r/s;
但我如何根据一天中的不同时间在 limit_req
中区分它们?
nginx 背后的服务是另一个具有上述限制的服务的 API 包装器。我的服务是用 Flask-RESTful
编写的,由 uWSGI
使用几个进程运行,因此实现限制逻辑并在进程之间同步它会非常痛苦,比如 redis
。
nginx可以这样配置吗?
如果不是,是否有任何常见的解决方法?其他服务如何解决这个任务?
更具体地说,它应该是一个泄漏的桶,所以客户端只是等待一个没有任何错误的答案,如 HTTP 429 太多请求。
答案
关键是绑定limit_req_zone
和map
。
文档中有两个关于它们的相关注释。 map:
default value
sets the resulting value if the source value matches none of the specified variants. When default
is not specified, the default resulting value will be an empty string.
limit_req_zone key zone=name:size rate=rate;
Sets parameters for a shared memory zone that will keep states for various keys. In particular, the state stores the current number of excessive requests. The key
can contain text, variables, and their combination. Requests with an empty key value are not accounted.
因此,只需同时使用 day 和 night limit_req_zone
with $server_name
as key 如果它应该工作,如果它不应该使用空字符串。 map
将 return $server_name
或空字符串,具体取决于一天中的时间。
map $date_gmt $day {
# 07:00-20:59 GMT
~(0[7-9]|1[0-9]|20):[0-5][0-9]:[0-5][0-9] $server_name;
}
map $date_gmt $night {
# 21:00-06:59 GMT
~(2[1-4]|0[0-6]):[0-5][0-9]:[0-5][0-9] $server_name;
}
limit_req_zone $day zone=day_zone:1m rate=1r/s;
limit_req_zone $night zone=night_zone:1m rate=4r/s;
...
limit_req zone=day_zone burst=100;
limit_req zone=night_zone burst=100;
关于req_limit
的一些注意事项
起初我尝试在limit_req
中使用映射变量,但是nginx不理解这样的语法。此外,乍一看 nginx -s reload
没有任何问题,但实际上没有任何重新加载,只有 service nginx restart
显示错误(nginx 版本:1.12.2)。所以我想展示一个 不应该做的事 :
limit_req_zone $server_name zone=day:1m rate=1r/s;
limit_req_zone $server_name zone=night:1m rate=4r/s;
map $date_gmt $time_of_day {
~(0[7-9]|1[0-9]|20):[0-5][0-9]:[0-5][0-9] day;
~(2[1-4]|0[0-6]):[0-5][0-9]:[0-5][0-9] night;
}
...
limit_req zone=$time_of_day burst=100;
关于性能的一些说明
这不是最佳解决方案,因为对每个请求都要进行两次检查。可以选择有两个单独的 nginx 配置,并使用 cron 之类的东西切换它们。这将是丑陋和错误的,因为你应该记住编辑两个配置,但它会工作得更快;我认为这个选项应该作为最后的手段使用。如果有横向扩展的能力,最好多台服务器负载均衡。
就我而言,这没什么大不了的:服务每小时收到大约 8k 个请求 (2.2 r/s)。
我想根据一天中的时间限制对我的服务的请求。
晚上 (21:00 - 06:59) 应该是 4 r/s,白天 (07:00-20:59) 应该是 1 r/s。
我可以有 2 个不同的 req_limit_zone
,比如
limit_req_zone $server_name zone=day:1m rate=1r/s;
limit_req_zone $server_name zone=night:1m rate=4r/s;
但我如何根据一天中的不同时间在 limit_req
中区分它们?
nginx 背后的服务是另一个具有上述限制的服务的 API 包装器。我的服务是用 Flask-RESTful
编写的,由 uWSGI
使用几个进程运行,因此实现限制逻辑并在进程之间同步它会非常痛苦,比如 redis
。
nginx可以这样配置吗?
如果不是,是否有任何常见的解决方法?其他服务如何解决这个任务?
更具体地说,它应该是一个泄漏的桶,所以客户端只是等待一个没有任何错误的答案,如 HTTP 429 太多请求。
答案
关键是绑定limit_req_zone
和map
。
文档中有两个关于它们的相关注释。 map:
default value
sets the resulting value if the source value matches none of the specified variants. When
default
is not specified, the default resulting value will be an empty string.
limit_req_zone key zone=name:size rate=rate;
Sets parameters for a shared memory zone that will keep states for various keys. In particular, the state stores the current number of excessive requests. The
key
can contain text, variables, and their combination. Requests with an empty key value are not accounted.
因此,只需同时使用 day 和 night limit_req_zone
with $server_name
as key 如果它应该工作,如果它不应该使用空字符串。 map
将 return $server_name
或空字符串,具体取决于一天中的时间。
map $date_gmt $day {
# 07:00-20:59 GMT
~(0[7-9]|1[0-9]|20):[0-5][0-9]:[0-5][0-9] $server_name;
}
map $date_gmt $night {
# 21:00-06:59 GMT
~(2[1-4]|0[0-6]):[0-5][0-9]:[0-5][0-9] $server_name;
}
limit_req_zone $day zone=day_zone:1m rate=1r/s;
limit_req_zone $night zone=night_zone:1m rate=4r/s;
...
limit_req zone=day_zone burst=100;
limit_req zone=night_zone burst=100;
关于req_limit
的一些注意事项
起初我尝试在limit_req
中使用映射变量,但是nginx不理解这样的语法。此外,乍一看 nginx -s reload
没有任何问题,但实际上没有任何重新加载,只有 service nginx restart
显示错误(nginx 版本:1.12.2)。所以我想展示一个 不应该做的事 :
limit_req_zone $server_name zone=day:1m rate=1r/s;
limit_req_zone $server_name zone=night:1m rate=4r/s;
map $date_gmt $time_of_day {
~(0[7-9]|1[0-9]|20):[0-5][0-9]:[0-5][0-9] day;
~(2[1-4]|0[0-6]):[0-5][0-9]:[0-5][0-9] night;
}
...
limit_req zone=$time_of_day burst=100;
关于性能的一些说明
这不是最佳解决方案,因为对每个请求都要进行两次检查。可以选择有两个单独的 nginx 配置,并使用 cron 之类的东西切换它们。这将是丑陋和错误的,因为你应该记住编辑两个配置,但它会工作得更快;我认为这个选项应该作为最后的手段使用。如果有横向扩展的能力,最好多台服务器负载均衡。 就我而言,这没什么大不了的:服务每小时收到大约 8k 个请求 (2.2 r/s)。