如何创建分布式"去抖动"任务以消耗Redis列表?

Gee*_*Jan 8 queue distributed mutex redis debouncing

我有以下用例:多个客户端推送到共享Redis列表.单独的工作进程应该排空此列表(处理和删除).Wait/multi-exec到位以确保,这很顺利.

出于性能原因,我不想立即调用'drain'进程,但是在x毫秒之后,从第一个客户端推送到(然后为空)列表的那一刻开始.

这类似于分布式下划线/ lodash 去抖动功能,计时器在第一个项目进入时开始运行(即:'领先'而不是'尾随')

我正在寻找以容错方式可靠地执行此操作的最佳方法.

目前我倾向于以下方法:

  1. 使用Redis的设置NXpx方法.这允许:
    • 仅将值(互斥锁)设置为专用键空间(如果尚不存在).这就是nx参数的用途
    • 在x毫秒后使密钥到期.这就是px参数的用途
  2. 1如果可以设置该值,则返回此命令,这意味着之前不存在任何值.0否则返回.A 1表示当前客户端是第一个运行该进程的客户端,因为Redis列表已耗尽.因此,
  3. 此客户端将作业放在分布式队列上,该队列计划在x毫秒内运行.
  4. 在x毫秒之后,接收作业的工作人员开始排空列表的过程.

这在纸上起作用,但感觉有点复杂.是否有其他方法可以以分布式容错方式工作?

顺便说一句:Redis和分布式队列已经到位,所以我不认为将它用于此问题是一个额外的负担.

mis*_*ion 5

对此感到抱歉,但正常的回答将需要大量文本/理论。因为您的问题很好,所以您已经写了一个很好的答案:)

首先,我们应该定义术语。下划线/破折号的“反跳”应该在大卫·科巴乔(David Corbacho)的文章解释中学习:

防抖动:可以将其视为“将多个事件组合在一起”。想象您回家,进入电梯,门正在关闭...突然间,您的邻居出现在大厅里,试图跳上电梯。讲礼貌!并为他打开门:您正在取消电梯的离开。考虑到第三人可能再次发生相同的情况,依此类推……可能会将出发时间延迟了几分钟。

油门:将其视为阀门,它调节执行流程。我们可以确定某个函数在特定时间内可以被调用的最大次数。因此,在电梯类比中,您很有礼貌地允许人们进入10秒钟,但是一旦延迟过去,您就必须走!

您询问的debounce第一个元素是sinse,将被推送到列表中:

这样,类似于电梯。电梯到达第一人称后10分钟后,电梯应上升。多少人挤进电梯并不重要。

如果是分布式容错系统,则应将其视为一组要求:

  1. 在插入第一个元素(即创建列表)之后,必须在X时间内开始处理新列表。
  2. 工伤事故不应破坏任何东西。
  3. 无死锁。
  4. 无论工人人数多寡,都必须满足第一个要求。

也就是说,您应该知道(以分布式方式)-一组工作人员必须等待,否则您可以开始列表处理。我们一说出“分布式”和“容错”。这些概念总是与他们的朋友一起引领:

  1. 原子性(例如通过阻止)
  2. 保留

在实践中

在实践中,恐怕您的系统需要稍微复杂一些(也许您只是没有编写过,而您已经拥有了)。

您的方法:

  1. 通过SET NX PX使用互斥锁进行悲观锁定。NX是保证一次仅执行一个过程(原子性)。该PX保证,如果事情在这个过程中发生的锁被Redis的(约死锁定容错的一部分)公布。
  2. 所有工作人员都尝试捕获一个互斥锁(每个列表键),因此只有一个快乐,并会在X时间之后处理列表。此过程可以更新互斥锁的TTL(如果需要更多时间,如原先希望的那样)。如果进程崩溃,则互斥锁将在TTL之后解锁并与其他工作人员争夺。

我的建议

Redis围绕RPOPLPUSH构建的容错可靠队列处理

  • RPOPLPUSH项目从处理到特殊列表(每个工人每个列表)。
  • 处理项目
  • 从特别清单中删除项目

要求因此,如果工作人员崩溃了,我们总是可以将特殊列表中的损坏消息返回到主列表。Redis保证RPOPLPUSH / RPOP的原子性。也就是说,只有一群问题工人需要等待一段时间。

然后有两个选择。首先-如果有很多客户,而较少的工人则在工人方面使用锁定。因此,尝试将互斥锁锁定在worker中,如果成功,则开始处理。

反之亦然。每次执行LPUSH / RPUSH时,请使用SET NX PX(如果您有很多工作人员和一些推送客户端,则要“等N次才能从我弹出”解决方案)。所以推是:

SET myListLock 1 PX 10000 NX 
LPUSH myList value
Run Code Online (Sandbox Code Playgroud)

每个工作人员只需检查myListLock是否存在,我们就应该至少等待键TTL,然后再设置处理互斥锁并开始耗尽。