本文首发于:行者 AI
速率限度通常作为服务的进攻措施予以施行。服务须要爱护本身免得适度应用(无论是无意还是无心),从而放弃服务可用性。在 Flask 我的项目开发过程中,遇到了须要对接口进行限度的需要,又不想去造轮子,这时候就须要用到 Flask-Limiter 这个三方库。本文将对 Flask-Limiter 的应用进行具体阐明。
1. 装置
装置依赖环境。
pip install Flask==1.1.1 Flask-Limiter==1.4
2. 疾速开始
有两种形式示意速率限度:
- “100 per day”、”20 per hour”、”5 per minute”、”1 per second”
- “100/day”、”20/hour”、”5/minute”、”1/second”
速率限度能够设置全局配置,针对所有接口进行限度;也能够通过装璜器进行部分限度;对于不想限度的接口,能够通过装璜器 @limiter.exempt
进行解除限制。示例代码如下所示:
app = Flask(__name__)
# 该配置为全局配置、实用于所有接口
limiter = Limiter(app, key_func=get_remote_address, default_limits=["100 per day", "10/hour"])
# @limiter.limit: 将笼罩全局 limiter 配置
@app.route("/slow")
@limiter.limit("1 per day")
def slow():
return ":("
# override_defaults: 示意该 limiter 是否笼罩全局 limiter 限度,默认为 True
@app.route("/medium")
@limiter.limit("1/second", override_defaults=False)
def medium():
return ":|"
# 残缺继承全局 limiter 配置
@app.route("/fast")
def fast():
return ":)"
# @limiter.exempt: 被装璜的视图不受全局速率限度
@app.route("/ping")
@limiter.exempt
def ping():
return "PONG"
3. 装璜器
依据集体爱好和应用场景,有以下几种形式:
繁多润饰:限度字符串能够是单个限度,也能够是定界符分隔的字符串。
@app.route("....")
@limiter.limit("100/day;10/hour;1/minute")
def my_route()
...
多个装璜器:限度字符串能够是单个限度,也能够是定界符分隔的字符串,也能够是两者的组合。
@app.route("....")
@limiter.limit("100/day")
@limiter.limit("10/hour")
@limiter.limit("1/minute")
def my_route():
...
自定义性能:默认状况下,依据 Limiter 实例初始化时所应用的要害性能来利用速率限度。开发者能够实现本人的性能。
def my_key_func():
...
@app.route("...")
@limiter.limit("100/day", my_key_func)
def my_route():
...
动静加载限度的字符串:在某些状况下,须要从代码内部的源(数据库,近程 api 等)中检索速率限度。这能够通过向装璜器提供可调用对象来实现。
留神:所装璜的路由上每个申请都会调用提供的可调用对象,对于低廉的检索,请思考缓存响应。
def rate_limit_from_config():
return current_app.config.get("CUSTOM_LIMIT", "10/s")
@app.route("...")
@limiter.limit(rate_limit_from_config)
def my_route():
...
4. 限度域
指依据什么进行限度,对应的参数为 key_func
,flask_limiter.util
提供了两种形式:
flask_limiter.util.get_ipaddr()
:应用X-Forwarded-For
标头中的最初一个 IP 地址,否则回退到申请的remote_address
。flask_limiter.util.get_remote_address()
:应用申请的remote_address
。
留神:在实在开发中,大部分我的项目都配置了 Nginx,如果间接应用 get_remote_address,获取到的是 Nginx 服务器的地址,相当于来自该 Nginx 服务器的所有申请会被当做同一个 IP 拜访,所以我的项目中个别都是自定义 key_func!
def limit_key_func():
return str(flask_request.headers.get("X-Forwarded-For", '127.0.0.1'))
获取 IP 的根据是依据 Nginx 的配置决定的:
X-Forwarded-For # 个别是每一个非通明代理转发申请时会将上游服务器的 ip 地址追加到 X -Forwarded-For 的前面,应用英文逗号宰割;X-Real-IP # 个别是最初一级代理将上游 ip 地址增加到该头中;X-Forwarded-For # 是多个 ip 地址,而 X -Real-IP 是一个;# 如果只有一层代理,这两个头的值就是一样的。
5. 共享限度
实用于速率限度由多条路由共享的状况。
命名共享限度:通过雷同的 shared_limit 对象进行装璜。
mysql_limit = limiter.shared_limit("100/hour", scope="mysql")
@app.route("..")
@mysql_limit
def r1():
...
@app.route("..")
@mysql_limit
def r2():
...
动静共享限度:将可调用对象作为范畴传递,该函数的返回值将用作范畴。留神,callable 具备一个参数:示意申请端点的字符串。
def host_scope(endpoint_name):
return request.host
host_limit = limiter.shared_limit("100/hour", scope=host_scope)
@app.route("..")
@host_limit
def r1():
...
@app.route("..")
@host_limit
def r2():
...
6. 贮存后端
记录 IP 拜访次数,用于判断该 IP 拜访次数是否达到限度。Limiter 默认应用内存作为贮存后端,然而在理论开发中,可能会波及到多过程资源不共享、服务器内存耗费等问题,个别是应用 redis 作为贮存后端。
- 须要 redis 服务器的地位,以及可选的数据库号。
redis://localhost:6379
或redis://localhost:6379/n
(对于数据库 n)。 - 如果 redis 服务器正在通过 unix 域套接字监听,则能够应用
redis+unix:///path/to/sock
或redis+unix:///path/to/socket?db=n
(对于数据库 n)。 - 如果数据库受密码保护,则能够在 URL 中提供明码,例如,
redis://:foobared@localhost:6379
或者redis+unix//:foobered/path/to/socket
。
# LIMITS_REDIS_STORAGE 就是上文提到的 redis 的 URI
limiter = Limiter(default_limits=["100 per day"], key_func=limit_key_func, storage_uri=LIMITS_REDIS_STORAGE)
7. 总结
通过对 API 申请的限度和配额,在肯定水平上能防止零碎收到超出其解决能力的数据,确保零碎的资源能失去正当的应用。以上就是本文的全部内容,心愿能给大家学习带来帮忙。