在Redis数据库中实现分布式速率限制的方法

 更新时间:2015年06月28日 11:59:29   投稿:goldensun  
这篇文章主要介绍了在Redis数据库中实现分布式速率限制的方法,文中展示了一个用Python编写的应用示例,需要的朋友可以参考下

问题

在许多应用中,对昂贵的资源的访问必须加以限制,此时速率限制是必不可少的。许多现代网络应用程序在多个进程和服务器上运行,状态需要被共享。一个理想的解决方案应该是高效、 快捷的,而不是依赖于被绑定到特定客户端的单个应用程序服务器(由于负载平衡) 或本身持有任何状态。
解决方案

实现这一目标的一个简单有效的方法就是使用 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客户端库。

相关文章

  • 单线程Redis快的4 个原因总结

    单线程Redis快的4 个原因总结

    作为内存中数据存储,Redis 以其速度和性能着称,通常被用作大多数后端服务的缓存解决方案,但是,在内部,Redis 采用单线程架构,为什么单线程设计依然会有这么高的性能,在本文中,让我们深入探讨为什么 Redis 才有单线程架构
    2023-07-07
  • 基于Redis实现延时队列的优化方案小结

    基于Redis实现延时队列的优化方案小结

    本文主要介绍了基于Redis实现延时队列的优化方案小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Redis实现集群搭建+集群读写的示例

    Redis实现集群搭建+集群读写的示例

    本文介绍了Redis集群的搭建和读写操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • Redis中的延迟双删

    Redis中的延迟双删

    这篇文章主要介绍了Redis中的延迟双删问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Redis进行缓存操作的实现

    Redis进行缓存操作的实现

    本文主要介绍了Redis进行缓存操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-03-03
  • redis集群实现清理前缀相同的key

    redis集群实现清理前缀相同的key

    这篇文章主要介绍了redis集群实现清理前缀相同的key,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Redis增减库存避坑的实现

    Redis增减库存避坑的实现

    在电商平台或者仓库管理系统中,库存的管理是非常重要的一项任务,本文主要介绍了Redis增减库存避坑的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • Python交互Redis的实现

    Python交互Redis的实现

    本文主要介绍了Python交互Redis的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • 浅谈Redis如何应对并发访问

    浅谈Redis如何应对并发访问

    本文主要介绍了Redis如何应对并发访问,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • Redis在计数器和人员记录的事务操作应用小结

    Redis在计数器和人员记录的事务操作应用小结

    Redis是一个高性能的键值存储系统,专于处理计数器和事务操作,它提供了INCR、DECR等命令来进行原子递增或递减操作,并通过MULTI、EXEC等命令实现事务操作,此外,Redis的Pipeline功能可减少网络往返次数,提高性能
    2024-10-10

最新评论