用redis实现防洪
Implement flood control with redis
我正在尝试用基于 Redis 的实现替换 sql Drupal 8 防洪服务的实现。
见https://github.com/drupal/drupal/blob/8.0.x/core/lib/Drupal/Core/Flood/DatabaseBackend.php
要求是这样的:
- 每次出现 action/event(例如尝试登录)都会记录到期时间、标识符和时间戳
- 我需要能够防止某个动作在给定时间范围内被执行超过 N 次
- 我希望能够清理过期事件
- 在 10 分钟内 3 的阈值的情况下,如果用户尝试一次,然后在 5 分钟后尝试两次,他将被阻止并可以在 5 分钟后重试一次。不是 10。虽然第二种方法是执行此操作的有效方法,但它不是 sql 实现的工作方式或测试期望它如何工作。
- 根据API可以看出,我在注册事件的时候也不知道阈值是多少,我只知道单个事件的过期时间。
我对如何实现这个的想法:
- 如果在给定时间内锁定 N 次事件之后,那么使用递增的 event:identifier 的单个 KEY 就很容易,一旦达到最大值,它就会被锁定直到再次过期并且每个 INCR 也会更新到期时间(或不更新)。
- 我发现很多帖子都询问列表条目是否过期,这是不可能的。有使用排序集和按范围删除的变通方法。大多数似乎都使用一个全局集,但我不能轻易地计算我的事件 + 标识符 - 我想。
在写下所有这些之后,我可能真的知道它是如何工作的,所以我想我正在寻找的是关于这是否有意义或者是否有更简单的方法的反馈。
每个 event:identifier 组合都是一个键并包含一个排序集。这使用过期作为分数和值作为唯一值,可能以微秒为单位的创建时间。我计算未过期的记录以检测是否达到阈值。我正在将每个 event:identifier 的到期时间更新为提供的到期时间 window,因此假设除非给定的 identifier/client 不放弃并继续尝试,否则它将被自动删除一直到到期。清理集合中的记录是否值得,例如什么时候做新登记?好像挺快的,我也只能偶尔做一下。
我更愿意使用 Redis 的密钥过期功能,而不是重新实现一个。
一个更简单的替代方案是:
- 只需设置一个简单的值,即尝试次数;使用建立在 "identifier":"event type" 等模式之上的密钥:
SETNX <identifier>:<event type> 1
如果响应为 1,这是第一次尝试,因此您在此键上设置超时:
EXPIRE <identifier>:<event type> <timeout in seconds>
否则你增加尝试次数
INCR <identifier>:<event type>
INCR 的响应将为您提供 window 期间的尝试次数,因此您知道是否可以允许该操作。
如果您需要存储更多数据,例如给定时间内允许的最大尝试次数 window,您也可以使用散列而不是简单值。在这种情况下,您可能会使用 HSETNX 和 HINCR。
我正在尝试用基于 Redis 的实现替换 sql Drupal 8 防洪服务的实现。
见https://github.com/drupal/drupal/blob/8.0.x/core/lib/Drupal/Core/Flood/DatabaseBackend.php
要求是这样的:
- 每次出现 action/event(例如尝试登录)都会记录到期时间、标识符和时间戳
- 我需要能够防止某个动作在给定时间范围内被执行超过 N 次
- 我希望能够清理过期事件
- 在 10 分钟内 3 的阈值的情况下,如果用户尝试一次,然后在 5 分钟后尝试两次,他将被阻止并可以在 5 分钟后重试一次。不是 10。虽然第二种方法是执行此操作的有效方法,但它不是 sql 实现的工作方式或测试期望它如何工作。
- 根据API可以看出,我在注册事件的时候也不知道阈值是多少,我只知道单个事件的过期时间。
我对如何实现这个的想法:
- 如果在给定时间内锁定 N 次事件之后,那么使用递增的 event:identifier 的单个 KEY 就很容易,一旦达到最大值,它就会被锁定直到再次过期并且每个 INCR 也会更新到期时间(或不更新)。
- 我发现很多帖子都询问列表条目是否过期,这是不可能的。有使用排序集和按范围删除的变通方法。大多数似乎都使用一个全局集,但我不能轻易地计算我的事件 + 标识符 - 我想。
在写下所有这些之后,我可能真的知道它是如何工作的,所以我想我正在寻找的是关于这是否有意义或者是否有更简单的方法的反馈。
每个 event:identifier 组合都是一个键并包含一个排序集。这使用过期作为分数和值作为唯一值,可能以微秒为单位的创建时间。我计算未过期的记录以检测是否达到阈值。我正在将每个 event:identifier 的到期时间更新为提供的到期时间 window,因此假设除非给定的 identifier/client 不放弃并继续尝试,否则它将被自动删除一直到到期。清理集合中的记录是否值得,例如什么时候做新登记?好像挺快的,我也只能偶尔做一下。
我更愿意使用 Redis 的密钥过期功能,而不是重新实现一个。
一个更简单的替代方案是:
- 只需设置一个简单的值,即尝试次数;使用建立在 "identifier":"event type" 等模式之上的密钥:
SETNX <identifier>:<event type> 1
如果响应为 1,这是第一次尝试,因此您在此键上设置超时:
EXPIRE <identifier>:<event type> <timeout in seconds>
否则你增加尝试次数
INCR <identifier>:<event type>
INCR 的响应将为您提供 window 期间的尝试次数,因此您知道是否可以允许该操作。
如果您需要存储更多数据,例如给定时间内允许的最大尝试次数 window,您也可以使用散列而不是简单值。在这种情况下,您可能会使用 HSETNX 和 HINCR。