手上有一个小项目,虽然只有几百个用户在使用,按道理说这种小型项目的话我的小水管可以轻松hold。但是结果就是我的服务器总是某名奇妙的卡住,发送的post请求一直堵塞,数据库的查询也异常缓慢,从日志里找了一下原因发现是自动化端的脚本写的着实有点鸡肋。
客户端的脚本会访问一个获取状态的接口,这个接口如果返回正常数据的话自动化设备就会正确执行,但是如果下发的是等待的请求的话自动化设备就会循环访问这个接口知道返回正常数据才停止。这不就是传说中的分布式拒绝服务攻击(DDOS)?虽然这些设备并不是分布式的,但是原理其实是一样的。设备不断地请求这个接口,如果访问间隔太短的话有很多次访问都是无意义的,并且会给服务器造成很大的压力,包括带宽和数据库方面。所以最好的方法就是给高频接口增加访问频率限制,高频访问的话就直接不处理返回一个对应的错误码或者自定义的错误信息。我想过滤掉一些高频访问问题应该能解决,直接改自动化测试脚本其实更快,但是我打不过写脚本的大哥。
自己写肯定是不会写,现成的轮子真是香,Flask-Limiter就是一个专门做接口限频的Flask扩展,只需要简单几步就可以配置到现成的项目当中。对于想要加以限制的路由直接加个装饰器在上面就行,和Flask-Login的使用十分类似,都是实现了相应的装饰器,装饰器的场景不要太喜欢。所以这个文章简单介绍一下Flask-Limiter的简单使用,使用都是基于文档的,如果想要了解详细配置可以直接参阅英文文档,文档短小精悍:https://flask-limiter.readthedocs.io/en/stable/。
Flask-Limiter的使用
官方简介:Flask-Limiter provides rate limiting features to flask routes. It has support for a configurable backend for storage with current implementations for in-memory, redis and memcache.
中文介绍:Flask-Limiter为flask路由提供了速率限制的特性。它支持使用内存、redis或者memcache作为存储环境的后端实现。
从官方的实现来看缓存数据可以存在内存,redis,或者memcache上,缓存的数据是作为频率限制的依据。虽然我项目中是直接存在内存里面的,配置只要三五行代码。但是好像直接使用内存有点低端,所以我准备试试存在redis上的话需要做哪些配置,不为别的,就是为了让大家知道这个项目用到了redis,用到了缓存,是蛮高端的项目。
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 "24" @app.route("/fast") def fast(): return "42" @app.route("/ping") @limiter.exempt def ping(): return "PONG"
上面是我从文章中直接复制过来的文档,上述代码中的Flask项目遵循以下几个访问频率限制特征:Ⅰ.频率限制的依据来自于request对象的remote_ip属性。Ⅱ.为所有的路由添加了默认的频率限制(200/天,50/小时)。Ⅲ.名为slow的路由将不被默认配置限制,它有自己独有的频率限制(1/天)。Ⅳ.名为ping的路由将不受limiter限制。
从QuickStart的代码就可以看出来其实Flask-Limiter的配置十分简单,如果是简单的使用只要把插件插入到我们的Flask项目之后并且给需要限制频率的路由打上装饰器就好了。上面代码会将产生的数据存在我们内存里面,如果想要存在Redis需要配置对应的storage_uri="redis://:123456@localhost:6379/0"
。可以在初始化Limiter的时候填入这个参数。值得注意的是:要想让limiter缓存在redis中必须安装相应的支持库,Flask-Limiter并不会自动安装,所以需要我们手动pip install flask-redis
。
但是一般我们会将相应的配置信息存在Flask的配置文件中,Liniter可以直接从Flask的config中读取相应的配置信息,这样写方便维护是比较推荐的写法。具体的配置信息可以通过查阅文档:Configuration。同样源码中Flask-Limiter有一个C类,寸的是相应的键值名称,查阅源码也是很有效的方式,我会给出一个简单的配置实例,可以在Raspberry项目查看。
可以看到启动项目之后访问对应的路由Redis已经有对应的缓存,值得注意的是Limiter对于static文件是没有访问限制的,上图的bootstrap并不是静态文件而是flask-bootstrap插件添加的路由,所以它同样受到默认的limiter限制。
速率限制文本表示法
速率限制文本参数应该遵循以下格式规范:[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
特别注意的是,如果提供的限制文本无法被解析或者是错误的格式,那么这个路由的限制将会被default设置的频率限制接管,同时会有相应的错误日志。
频率限制的错误处理
当客户端触发了频率限制规则,默认Flask-Limiter会返回一个429的错误状态码,这个页面会有一个简单的response对象。当然大部分人都会觉得这个界面有点丑,如果想自定义这个界面的话可以通过app_errorhandler装饰器实现,使用方法和处理404的做法是一样的,可以看看Github的代码例程。
当然上面介绍的仅仅是最最最基础的用法,Flask-Limiter还提供和很多更具体的细节功能包括:自定义的关键字函数,让Limiter使用自己定义的函数做限频判断;设置共享限频函数,实现多个函数使用一个限频器;自定义请求过滤函数,实现用户端的访问白名单;自定义返回的请求头...总是有一大堆的细节功能可以供我们选择,如果想要实现更加灵活的功能可以详细参考一下官方给的文档。
文字总是晦涩的,细节的使用还是参阅Github上面的代码,我会完成一个尽可能简单的小demo,方便有需要的朋友去学习查阅。
项目地址:https://github.com/Weiney/raspberry
Flask-Limiter文档地址:https://flask-limiter.readthedocs.io/en/stable
共有 0 条评论