在Redis数据库中实现分布式速率限制的方法
问题
在许多应用中,对昂贵的资源的访问必须加以限制,此时速率限制是必不可少的。许多现代网络应用程序在多个进程和服务器上运行,状态需要被共享。一个理想的解决方案应该是高效、 快捷的,而不是依赖于被绑定到特定客户端的单个应用程序服务器(由于负载平衡) 或本身持有任何状态。
解决方案
实现这一目标的一个简单有效的方法就是使用 Redis, 它有很多有用的数据结构和功能, 尽管实现速率限制只需要2个功能用: 一、在某个具体的键值上递增一个整数,二、给这个键值设置过期时间。
因为redis 有个单一的事件循环系统 (每个人每次在同一个时间只能执行一个操作),这是个原子操作, 也就是说无论有多少个客户端同时交互操作,对于同一个键值总有一个确定的数值。
这在对同一个资源进行多个速率限制的情况下通常是有利的, 因为这允许少量的破裂,以及更长的期限限制。例如每秒钟请求3次,没分钟请求20次。因为每个限制都是相对独立的,这就需要与其它限制分开进行单独的递增。
因为速率限制通常用在响应时间比较重要的资源(比如网页应用),所以尽量缩短速率限制的使用时间是非常有必要的。redis的最基本的应用就是发出命令,等待响应,然后发出另一个命令,如此往复。 这个花费是昂贵的,因为需要通过网络在应用程序和redis服务器之间多次往返。由于在这个用例中,没有命令依赖其它命令的执行结果,这使得redis的一个叫做流水线技术的使用成为可能。这就是客户端缓存所有redis请求,然后把这写请求发送给redis,redis一次性返回所有的结果。
Redis不会维护客户端需要的限制的,因为redis会根据客户端设置的过期时间删除旧的记数。这消除了客户端统筹协调的需要,和删除竞争条件的可能性。
The Code
import redis import time def rate_limit_check(r, key, limits): period_lengths = [_[0] for _ in sorted(limits.items())] period_limits = [_[1] for _ in sorted(limits.items())] pipe = r.pipeline() for period_length in period_lengths: current_period = int(time.time() / period_length) redis_key = "rate_limit:{key}:{period_length}:{current_period}".format(key=key, period_length=period_length, current_period=current_period) pipe.incr(redis_key).expire(redis_key, period_length*3) return not any(hits period_limit for period_limit, hits in zip(period_limits, pipe.execute()[::2])) if __name__ == "__main__": r = redis.Redis() print rate_limit_check(r, "127.0.0.1", {1: 3, 60: 20})
{1: 3, 60: 20} 意味着每秒钟3次的命中率是允许的,在任何限制下,都允许20次的命中。 127.0.0.1 在这里用作键值,尽管在真实的情况下,可能作为IP地址。更高级的用例将有一个全应用程序的速率限制,键值只有客户端的IP地址,以及一个为昂贵的终结点设置的特定终结点限制,这将用到客户端的IP地址和终结点,例如127.0.0.1+/login/。这些限制可以独立地设置。
return rate_limit_check(r, "127.0.0.1", {1: 3, 60: 20}) and rate_limit_check(r, "127.0.0.1+/login/", {1: 2, 60: 5})
这是一个用Python写的例子,它可以简单地移植到任何语言,只要这门语言包含Redis客户端库。
我想要获取技术服务或软件
服务范围:MySQL、ORACLE、SQLSERVER、MongoDB、PostgreSQL 、程序问题
服务方式:远程服务、电话支持、现场服务,沟通指定方式服务
技术标签:数据恢复、安装配置、数据迁移、集群容灾、异常处理、其它问题
本站部分文章参考或来源于网络,如有侵权请联系站长。
数据库远程运维 在Redis数据库中实现分布式速率限制的方法
相关文章
- 基于Redis脚本实现交互式数据库管理(redis脚本)
- 宝塔:极大加速实现Redis性能优化(宝塔redis)
- Linux环境下安装Redis数据库(linux下安装redis)
- 使用Redis:理解默认数据库(redis默认数据库)
- 提升性能:Redis 内存数据库的优势(redis内存数据库)
- Redis:数据库的替代品(redis替代数据库)
- Redis队列:高效操作数据的必备技能(redis队列操作)
- 从Redis加载数据库:新的更新方式(redis加载数据库)
- 深入分析Redis慢查询原因(redis的慢查询)
- Redis实现高效数据去重方法(redis去重)
- Redis数据库的安全备份与恢复(redis 备份命令)
- Redis:高效实用的消息队列实现方法(redis如何做消息队列)
- 学习进入Redis容器的简单方法(怎么进入redis容器)
- 提升Redis数据库性能如何设置时间(怎么给redis设置时间)
- 序列化痛苦Redis Operator中的挑战(序列化问题redis)
- 通过Redis获取最佳值的方法(获取值 redis)
- 查看Redis键值大小快速有效的方法(查看redis键值大小)
- 查看Redis中所有值的方法(查看redis的所有值)
- 本机轻松搭建Redis数据库(本机安装redis)
- 破解本地Redis服务器登录的技巧(本地怎么登陆redis)
- 深入探索Redis分布式系统的奥秘(深入理解redis分布式)
- 深入研究Redis框架的数据库缓存技术(数据库缓存redis框架)
- 为何Redis成为高性能缓存数据库的首选(为何redis)
- 前端如何连接 Redis 数据库(前端怎么连接redis)
- 极速解决如何自动启动Redis数据库(怎么自动启动redis库)
- 使用Redis集群打造大数据环境(大数据redis集群安装)
- 使用Redis解决限流问题(使用redis做限流)
- 深入浅出Redis精通Redis数据库开发(关于redis书籍)
- 如何避免Redis雪崩(redis雪崩如何避免)
- 理解Redis脑裂分布式计算最大挑战(什么是redis脑裂问题)
- 手动控制Redis集群的切换过程(redis 集群手动切换)
- Redis集群实现多租户安全管理(redis集群多租户)
- 缓慢的Redis连接调查与优化(redis连接缓慢)