本文首发于:行者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_funcflask_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:6379redis://localhost:6379/n(对于数据库n)。
  • 如果redis服务器正在通过unix域套接字监听,则能够应用redis+unix:///path/to/sockredis+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申请的限度和配额,在肯定水平上能防止零碎收到超出其解决能力的数据,确保零碎的资源能失去正当的应用。以上就是本文的全部内容,心愿能给大家学习带来帮忙。