本文首发于:行者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.exemptdef 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_limitdef r1(): ...@app.route("..")@mysql_limitdef r2(): ...
动静共享限度:将可调用对象作为范畴传递,该函数的返回值将用作范畴。留神,callable具备一个参数:示意申请端点的字符串。
def host_scope(endpoint_name): return request.hosthost_limit = limiter.shared_limit("100/hour", scope=host_scope)@app.route("..")@host_limitdef r1(): ...@app.route("..")@host_limitdef 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的URIlimiter = Limiter(default_limits=["100 per day"], key_func=limit_key_func, storage_uri=LIMITS_REDIS_STORAGE)
7. 总结
通过对API申请的限度和配额,在肯定水平上能防止零碎收到超出其解决能力的数据,确保零碎的资源能失去正当的应用。以上就是本文的全部内容,心愿能给大家学习带来帮忙。