Redis - 使用BRPOPLPUSH时更好的清理处理队列(可靠)的方法

时间:2015-01-16 14:54:13

标签: redis queue reliability

我们的当前设计

环境 Redis 2.8.17

我们使用类似于redis文档中描述的模式,在RPOPLPUSH

下实现了可靠的队列

但是,考虑到阻止性质,我们使用 BRPOPLPUSH ,并使用 LPUSH 来确保FIFO顺序。

制作人多个线程(来自多个服务器)使用 LPUSH 来推送项目。

消费者多个线程(来自多个服务器)使用 BRPOPLPUSH 来处理这些项目。

BRPOPLPUSH q processing-q

如记录所示,redis从队列'q'弹出项目,同时在'processing-q'中添加它们。

问题

由于我们的应用程序具有多线程(异步)特性,当 消费者 完成处理时,我们无法控制。< / p>

因此,如果我们使用 LREM (根据文档)从 processing-q ,这只会删除 processing-q 的顶部元素。哪里无法保证,是否已删除了由相应 消费者 处理的实际元素。

因此,如果我们不做任何事情,那么 processing-q 会继续增长(食用内存),这是非常糟糕的恕我直言。

有任何建议或想法吗?

4 个答案:

答案 0 :(得分:21)

您只需要在调用LREM时包含要删除的作业。

LREM采用以下形式:

LREM queue count "object"

它将从队列中删除等于&#34; object&#34; count 项。因此,要删除您的消费者线程正在处理的特定作业,请执行此类操作。

LREM processing-q 1 "job_identifier"

有关详细信息,请参阅此处的文档:http://redis.io/commands/lrem

然后,为了处理崩溃的消费者和已放弃的作业,您可以使用SETEX创建具有过期的锁定,并定期检查没有锁定的作业。

所以整个过程看起来像这样:

<强>生产者

  1. RPUSH q "job_identifier"
  2. <强>消费

    1. SETEX lock:processing-q:job_identifier 60(先设置锁定以避免竞争条件)
    2. BRPOPLPUSH q processing-queue
    3. 处理工作
    4. LREM processing-queue "job_identifier"
    5. 过期的工作监控

      1. jobs = LRANGE processing-queue 0 -1
      2. 工作中的foreach工作:lock = GET lock:processing-q:job_identifier
      3. 如果lock为null,则此作业超时,因此从处理中删除-q LREM processing-queue "job_identifier"
      4. 并使用RPUSH q "job_identifier"
      5. 重试

        @NotAUser发布了一个开源java实现,在这里:https://github.com/graknlabs/redisq/tree/master/src/main/java/ai/grakn/redisq

答案 1 :(得分:8)

我将采用的方法是使用每个消费者的处理q(例如 processing-q:consumer-id )。这将解决您当前的问题,但您仍然需要以某种方式处理崩溃的消费者。为此,我建议您保留每个消费者最后一次弹出任务并定期检查超时。如果使用者已达到超时,则将其任务移回主队列并删除其队列。

答案 2 :(得分:4)

在类似的项目中,我使用主机名和工作进程ID作为备份队列。每个工作人员都有自己的备份队列,如果工作人员死亡,则该项目不会丢失。

查看READMEimplementation了解详情。

答案 3 :(得分:0)

除了建议的解决方案之外,您还可以ltrim将处理队列设置为对您的服务有意义的数量。这将确保处理队列永远不会超出比例。

但如果达到修剪限制,你将开始丢失物品。这可能是也可能不适用于您的用例。

http://redis.io/commands/ltrim