Flask-Limit详细说明

在flask项目中我们需要对全部或者一部分接口进行限制,又不想造轮子,那怎么办呢?

所以这就是flask-limit出现的原因,不过对于相对复杂的需求,还是自己造轮子吧!

安装与简单使用

安装:pip install Flask-Limiter

快速开始:

from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address app = Flask(__name__)
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
@app.route("/slow")
@limiter.limit("1 per day")
def slow():
return ":(" @app.route("/medium")
@limiter.limit("1/second", override_defaults=False)
def medium():
return ":|" @app.route("/fast")
def fast():
return ":)" @app.route("/ping")
@limiter.exempt
def ping():
return "PONG"

上诉频率限制说明:

  • 默认通过请求的remote_address进行限制。
  • 默认限制为200次/天,50次/小时;适用于所有路线
  • slow路由的限制将绕过默认的速率限制,为1次/天
  • medium路由继承默认限制,并增加了1次/秒的限制
  • ping路由不受任何默认速率限制的约束

注意: 静态路由不受速率限制

每次请求超出速率限制时,将不会调用view函数,而是会引发429http错误。

速率限制规则:

[count] [per|/] [n (optional)] [second|minute|hour|day|month|year]

可以使用自己选择的分隔符将多个速率限制组合起来。

示例:

  • 10 per hour
  • 10/hour
  • 10/hour;100/day;2000 per year
  • 100/day, 500/7days

使用的详细说明

看完上面的部分其实已经满足大部分需求了,但是真实的情况下,可能还存在其他的定制服务,以下就是详细说明。

初始化

初始化有两种方式:

  1. 使用构造函数

    from flask_limiter import Limiter
    from flask_limiter.util import get_remote_address
    .... limiter = Limiter(app, key_func=get_remote_address)
  2. 使用延迟应用初始化 init_app

    limiter = Limiter(key_func=get_remote_address)
    limiter.init_app(app)

实际开发中更有可能使用的是延迟初始化。

装饰器

我们所使用的是已创建的Limiter示例的limit方法,可根据喜好和使用场景,有以下几种使用方式:

单装饰

@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():
...

新增自定义的功能

下方会有详细介绍此装饰器内的参数的说明

def my_key_func():
... @app.route("...")
@limiter.limit("100/day", my_key_func)
def my_route():
...

限制域

即指定根据什么进行限制,对应的参数为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服务器的地址,非常危险!!!

所以项目中很有可能都是自定义key_func!

搭载Nginx服务器的key_func示例:

def limit_key_func():
return str(flask_request.headers.get("X-Forwarded-For", '127.0.0.1'))

不过以上设置的依据还是根据Nginx的配置决定的,有兴趣的同学还可以了解一下X-Forwarded-ForX-Real-IP的区别。

X-Forwarded-For 一般是每一个非透明代理转发请求时会将上游服务器的ip地址追加到X-Forwarded-For的后面,使用英文逗号分割 ;

X-Real-IP一般是最后一级代理将上游ip地址添加到该头中 ;

X-Forwarded-For是多个ip地址,而X-Real-IP是一个;

如果只有一层代理,这两个头的值就是一样的。

所以上方自定义的方法仅作参考。

动态加载限制字符串

常见的限制规则已在上文介绍过,这里介绍的在某些情况下,需要从代码外部的源(数据库,远程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():
...

所装饰的路由上的每个请求都会调用提供的可调用对象。对于昂贵的检索,请考虑缓存响应。

豁免条件

个人觉得这可以从两个方面来谈,一是针对key,一是针对计次,以下我们分别进行介绍。

  • 白名单:

    • 方式一:参数为exempt_when,设置这个参数将不被频率限制。

      @app.route("/expensive")
      @limiter.limit("100/day", exempt_when=lambda: current_user.is_admin)
      def expensive_route():
      ...
    • 方式二:请求过滤器Limiter.request_filter()方法(没研究)

      @limiter.request_filter
      def header_whitelist():
      return request.headers.get("X-Internal", "") == "true" @limiter.request_filter
      def ip_whitelist():
      return request.remote_addr == "127.0.0.1"
  • 不计次情况:参数为deduct_when,判断某些情况不计入使用频率的次数。

    def func_deduct(response):
    """
    频率限制之根据response决定是否计次
    :param response: flask.wrappers.Response对象
    :return: 计次返回True
    """
    # 正常响应状态码:200
    res = response.response if response._status_code == 200 else None
    if res:
    res = json.loads(res[0])
    # 有响应数据,记一次
    return res.get("code") == 200 return False @api.route('/captcha')
    @limit.limit("5/day;3/hour", deduct_when=func_deduct)
    def expensive_route():
    ...
  • 路由豁免:此情况特殊,属于某个路由不参与频率限制,使用方式为

    limiter.exempt()

共享限制

适用于速率限制应由多条路由共享的情况。

命名共享限制

mysql_limit = limiter.shared_limit("100/hour", scope="mysql")

@app.route("..")
@mysql_limit
def r1():
... @app.route("..")
@mysql_limit
def r2():
...

动态共享限制:将可调用对象作为范围传递时,该函数的返回值将用作范围。

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():
...

共享限制使用上与单个限制一致

配置

参数 说明
RATELIMIT_DEFAULT 默认策略, 逗号分隔('1/minute,100/hour')
RATELIMIT_DEFAULTS_PER_METHOD 是按方法/路线应用默认限制,还是按方法将所有方法组合应用默认限制。
RATELIMIT_DEFAULTS_EXEMPT_WHEN 默认豁免条件
RATELIMIT_APPLICATION 应用策略,用于将限制应用于整个应用程序(即,由所有路由共享)。
RATELIMIT_STORAGE_URL 存储位置:
  • 内存:memcached://host:port
  • Redis: redis://host:port |

    | RATELIMIT_STORAGE_OPTIONS | 一个字典,用于设置要在初始化时传递给存储实现的其他选项。 |

    | RATELIMIT_STRATEGY | 使用的限速策略。详见限速策略 |

    | RATELIMIT_HEADERS_ENABLED | 是否返回速率限制的相关信息到reponse header中。默认为False,与上一条一样可以忽视。 |

    | RATELIMIT_ENABLED | 速率限制的总体终止开关。默认为True |

    | RATELIMIT_HEADER_LIMIT | 当前速率限制的标题。默认为X-RateLimit-Limit |

    | RATELIMIT_HEADER_RESET | 当前速率限制的重置时间的标题。默认为X-RateLimit-Reset |

    | RATELIMIT_HEADER_REMAINING | 当前速率限制中剩余的请求数的标头。默认为X-RateLimit-Remaining |

    | RATELIMIT_HEADER_RETRY_AFTER | 客户端应何时重试请求的标头。默认为Retry-After |

    | RATELIMIT_SWALLOW_ERRORS | 默认False即可 |

    | RATELIMIT_IN_MEMORY_FALLBACK_ENABLED | 如果启用,则当配置的存储关闭时,内存中的速率限制器将用作备用。与RATELIMIT_IN_MEMORY_FALLBACK原始速率限制结合使用时,将不会继承该限制 |

    | RATELIMIT_IN_MEMORY_FALLBACK | 后端存储异常使用的策略配置 |

    | RATELIMIT_KEY_PREFIX | 存储key的前缀配置 |

速度限制策略

Flask-Limiter内置了三种不同的速率限制策略。

分别为: Fixed Window、Fixed Window with Elastic Expiry、Moving Window

暂未研究,不做介绍。

错误响应

超出限制的请求返回的都是429状态码,示例如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>429 Too Many Requests</title>
<h1>Too Many Requests</h1>
<p>1 per 1 day</p>

如果要配置响应,可对路由状态码判断后响应,示例如下:

@app.errorhandler(429)
def ratelimit_handler(e):
return make_response(
jsonify(error="ratelimit exceeded %s" % e.description)
, 429
)

当然,还可以自定义错误信息:

app = Flask(__name__)
limiter = Limiter(app, key_func=get_remote_address) def error_handler():
return app.config.get("DEFAULT_ERROR_MESSAGE") @app.route("/")
@limiter.limit("1/second", error_message='chill!')
def index():
.... @app.route("/ping")
@limiter.limit("10/second", error_message=error_handler)
def ping():
....

CBV与Blueprint使用

FBV可以使用装饰器的方式进行限制,但是对于CBV就有些不适用了,以下就是CBV的使用方式。

app = Flask(__name__)
limiter = Limiter(app, key_func=get_remote_address) class MyView(flask.views.MethodView):
decorators = [limiter.limit("10/second")]
def get(self):
return "get" def put(self):
return "put"

CBV的方式还是有些麻烦了,如果能对蓝图下所有的路由都进行限制就更好了,也可以对某个蓝图进行豁免。

app = Flask(__name__)
login = Blueprint("login", __name__, url_prefix = "/login")
regular = Blueprint("regular", __name__, url_prefix = "/regular")
doc = Blueprint("doc", __name__, url_prefix = "/doc") @doc.route("/")
def doc_index():
return "doc" @regular.route("/")
def regular_index():
return "regular" @login.route("/")
def login_index():
return "login" limiter = Limiter(app, default_limits=["1/second"], key_func=get_remote_address)
limiter.limit("60/hour")(login)
limiter.exempt(doc) app.register_blueprint(doc)
app.register_blueprint(login)
app.register_blueprint(regular)

关于代理

虽然上文说过Nginx代理的情况需要更复杂的操作,不过在查看官方文档的时候,还发现了一个简单的方法,说明如下:

如果您的应用程序位于代理之后,并且您使用的是werkzeug> 0.9+,则可以使用werkzeug.contrib.fixers.ProxyFix 修复程序可靠地获取用户的远程地址,同时保护您的应用程序免于通过标头进行ip欺骗。

from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from werkzeug.contrib.fixers import ProxyFix app = Flask(__name__)
# for example if the request goes through one proxy
# before hitting your application server
app.wsgi_app = ProxyFix(app.wsgi_app, num_proxies=1)
limiter = Limiter(app, key_func=get_remote_address)

API

flask_limit.Limiter类初始化属性,Limiter(app=None, key_func=None, global_limits=[], default_limits=[], default_limits_per_method=False, default_limits_exempt_when=None, default_limits_deduct_when=None, application_limits=[], headers_enabled=False, strategy=None, storage_uri=None, storage_options={}, auto_check=True, swallow_errors=False, in_memory_fallback=[], in_memory_fallback_enabled=False, retry_after=None, key_prefix='', enabled=True)

参数 说明
app 即flask的项目
key_func 限制域
default_limits 默认限制策略
default_limits_per_method 默认限制是按方法/路线应用还是按每种方法所有方法的组合应用。
default_limits_exempt_when 默认豁免条件
default_limits_deduct_when 接收response对象并返回True / False以决定是否应从默认速率限制中扣除的函数
application_limits 所有路由的共享限制
headers_enabled 是否写入响应头
storage_uri 存储位置
storage_options 意义不明的额外配置
auto_check 是否自动检查应用程序的before_request链中的速率限制。默认True
swallow_errors 达到速率限制时会记录异常。默认False
in_memory_fallback 字符串或可调用项的可变列表,返回表示存储空间不足时要应用的回退限制的字符串
in_memory_fallback_enabled 仅在主存储关闭并继承原始限制时才退回到内存存储中。
key_prefix 前缀
strategy 策略

方法:

check()

exempt()

ini_app()

request_filter()

reset()

limit(limit_value, key_func=None, per_method=False, methods=None, error_message=None, exempt_when=None, override_defaults=True, deduct_when=None)

shared_limit(limit_value, scope, key_func=None, error_message=None, exempt_when=None, override_defaults=True, deduct_when=None)

Flask-Limit使用详细说明的更多相关文章

  1. 服务器配置:ECS+Nginx+uWSGI+Flask——各部分详细介绍

    希望在阿里云ECS上搭建一个flask框架的web应用,经典的形式便是flask+uWSGI+nginx模式 服务器:CentOS 7.3 python版本:3.8.0 先贴一张全局图,这张图很清楚的 ...

  2. windows下python+flask环境配置详细图文教程

    本帖是本人在安装配置python和flask环境时所用到的资源下载及相关的教程进行了整理罗列,来方便后面的人员,省去搜索的时间.如果你在安装配置是存在问题可留言给我. 首先罗列一下python+fla ...

  3. flask-admin章节四:flask session的使用

    1. 关于session flask session可能很多人根本都没有使用过,倒是cookie大家可能使用得比较多.flask cookie使用起来比较简单,就两个函数,读取和设置. 具体使用方式如 ...

  4. sqlalchemy模块介绍、单表操作、一对多表操作、多对多表操作、flask集成.

    今日内容概要 sqlalchemy介绍和快速使用 单表操作增删查改 一对多 多对多 flask集成 内容详细 1.sqlalchemy介绍和快速使用 # SQLAlchemy是一个基于 Python实 ...

  5. python Tornado(招聘的一个比较经常问到的知识)

    Tornado既是一个webserver也是一个web框架 这是一个总结的比较详细的内容 http://www.nowamagic.net/academy/detail/1332612 开源中国中的关 ...

  6. 【转】持久化消息队列之MEMCACHEQ

    G MEMCACHEQ AS MESSAGE QUEUE PHP,消息队列,MEMCACHEQ 使用消息队列(MESSAGE QUEUE)可以把某些耗时的工作推后,然后在后台慢慢地去执行,这样就不会让 ...

  7. EXTJS 4.2 资料 控件之Grid 行编辑绑定下拉框,并点一次触发一次事件

    主要代码: { header: '属性值', dataIndex: 'PropertyValueName', width: 130, editor: new Ext.form.field.ComboB ...

  8. mysq常用l性能分析方法

    orzdba查看读写./orzdba.pl --mysql -S /data/mysql30001/mysql.sock 语句查看读写命令数量,以及数据库TPS,传输的大小 查看processlist ...

  9. python gunicorn详解

    Gunicorn是一个unix上被广泛使用的高性能的Python WSGI UNIX HTTP Server.和大多数的web框架兼容,并具有实现简单,轻量级,高性能等特点. gunicorn 安装 ...

  10. AI+云原生,把卫星遥感虐的死去活来

    摘要:遥感影像,作为地球自拍照,能够从更广阔的视角,为人们提供更多维度的辅助信息,来帮助人类感知自然资源.农林水利.交通灾害等多领域信息. 本文分享自华为云社区<AI+云原生,把卫星遥感虐的死去 ...

随机推荐

  1. airpods2隐藏的使用技巧(十)点

    airpods的凭借出色的外观.不错的音质以及非常人性化的用户体验秒杀了同类型的许多真无线蓝牙耳机,以下是第二代产品airpods2一些使用的技巧,推荐给大家.   一. 随时随地查看airpods2 ...

  2. MATLAB作图之二

    "平滑"二维图像可以通过对图像进行插值实现.那么对于一条有大量"毛刺"的曲线,是不是也可以通过插值来平滑呢?答案是肯定的. "平滑"前 x ...

  3. Ubuntu安装Vmware Tools解决屏幕比例失调

    前言 安装ubuntu虚拟机时默认比例如下图,且ubuntu系统选项中没有合适的比例,可以安装Vmware Tools来解决. 注意:该方法只适用于有操作界面的系统,之前有位小伙伴在服务器上也想安装T ...

  4. TensorFlow从0到1之TensorFlow多层感知机函数逼近过程(23)

    Hornik 等人的工作(http://www.cs.cmu.edu/~bhiksha/courses/deeplearning/Fall.2016/notes/Sonia_Hornik.pdf)证明 ...

  5. Windows程序设计(2) - API-02 文件系统

    一.磁盘分区的基本概念 1.磁盘分区(Patitions): 分区就是物理存储设备分割成多个不同的逻辑上的存储设备.分区从实质上说就是对硬盘的一种格式化.当我们创建分区时,就已经设置好了硬盘的各项物理 ...

  6. spring源码分析——BeanPostProcessor接口

    BeanPostProcessor是处理bean的后置接口,beanDefinitionMaps中的BeanDefinition实例化完成后,完成populateBean,属性设置,完成 初始化后,这 ...

  7. WeChair——团队展示

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 团队名称 WeChair 这个作业要求在哪里 团队作业第一次 这个作业的目标 团队合作,项目开发 作业正文 如下 其他参考文献 ...

  8. Kubernetes学习笔记(九):StatefulSet--部署有状态的多副本应用

    StatefulSet如何提供稳定的网络标识和状态 ReplicaSet中的Pod都是无状态,可随意替代的.又因为ReplicaSet中的Pod是根据模板生成的多副本,无法对每个副本都指定单独的PVC ...

  9. 一、Jenkins 安装(自动构建发布)

    war 包方式安装 官方下载地址:https://jenkins.io/download/ ,下载war包,并上传到服务器(案例中是把war包放在了 /usr/local/jenkins 里面) 运行 ...

  10. SpringCloud教程第1篇:Eureka(F版本)

    一.创建服务注册中心(Eureka组件) 1.1 首先创建一个maven主工程. 1.创建maven项目 是一个主Maven工程,spring Boot版本为2.0.3.RELEASE,Spring ...