简单地说,我们现在是Redis

了解更多

复述,最佳实践

事件队列

Redis Lists是一个字符串的有序列表,非常类似于你可能熟悉的链表。推入、向列表添加值和弹出、从列表中删除值(从左/头/开始或右/尾/结束)都是非常轻量级的操作。可以想象,这是管理队列的一个非常好的结构:对于先进先出(FIFO)队列,将项添加到列表的头部,并从尾部读取项。

Redis还提供了额外的功能,使这种类型的模式更有效,可靠,易于使用。列表命令的一个子集允许“阻塞”行为。术语“阻塞”只适用于单个连接的客户端——实际上,这些命令会阻止客户端执行任何操作,直到列表中有一个值(或超时)。这消除了需要投票的Redis结果。由于客户端在等待值时不能做任何事情,我们需要两个打开的客户端来说明这一点:

R 发送端 阻塞客户端
1
>BRPOPmy-q 0(等待)
2
>LPUSHMy-q hello(整数)1
1)“my-q”2)"hello"[准备好执行命令]
3.
>BRPOPmy-q 0(等待)

在第1行示例中,我们看到阻塞的客户端不会立即返回任何东西,因为列表(" my-q ")中没有任何值。最后一个参数是timeout -在本例中,0意味着它永远不会超时并将永远等待。第二行,发送客户端向my-q键发出LPUSH,另一个客户端立即结束阻塞。在第三行,我们可以发出另一个BRPOP命令(通常在客户机语言中通过循环来完成),并等待任何进一步的列表值。您可以使用ctrl-c在redis-cli中退出阻塞。

让我们反转这个例子,看看BRPOP如何处理一个非空列表:

R 发送端 阻塞客户端
1
> LPUSH my-q hello (integer) 1 .使用实例
2
> LPUSH my-q hej (integer) 2
3.
> LPUSH my-q bonjour (integer) 3
4
> BRPOP my-q 1)“my-q”2)“你好”
5
> BRPOP my-q 1)“my-q”2)“hej”
6
>BRPOP my-q 0 1)“my-q”2“你好”
7
> BRPOP my-q 0[等待]

在第1-3行中,我们将3个值放入列表中,我们可以看到响应在增长(表示列表中的项目数量)。第4行,尽管发出了BRPOP命令,还是立即返回值?为什么呢?因为阻塞行为只适用于队列中没有项的情况。我们可以在第5-6行看到相同的立即响应,因为它遍历队列中的每一项。在第7行中,BRPOP遇到一个空队列并阻塞,直到将项目添加到队列中。

队列通常表示需要在另一个流程(工作者)中完成的某种工作。这类工作负载的关键在于,如果工作人员在处理过程中出现故障并因某种原因死亡,作业不会丢失。Redis也可以支持这种类型的队列。替换为BRPOPLPUSH,而不是使用BRPOP。BRPOPLPUSH(多大的一口)等待一个列表中的值,一旦得到一个值,它就会将其推送到另一个列表中。这都是原子完成的,因此两个工人不可能删除/获取相同的值。让我们看看这是如何工作的:

R 发送端 阻塞客户端
1
>LINDEX工人-q 0(无)
2
[如果第一行的结果不是nil,对它做一些事情,否则跳转到4]
3.
>LREMWorker-q -1 [value from 1] (integer) 1 [loop back to 1]
4
>BRPOPLPUSHmy-q worker-q 0[等待]
5
> LPUSH my-q你好
"hello"[准备好执行命令]
6
[用“你好”做点什么]
7
> LREM worker-q -1 hello (integer) 1
8
[循环到第1行]

在第1行和第2行中,我们还没有做任何事情,因为worker-q是空的。如果woker-q中出现了某些内容,我们将对其进行处理并将其删除,然后跳回1以查看队列中是否还有其他内容。通过这种方式,我们首先清除工作队列,然后执行任何已经存在的作业。在第4行中,我们等待一个值被添加到my-q,当我们得到一个值时,它被原子地添加到worker-q。接下来,我们对“hello”执行一些类型的非Redis操作,完成后,我们使用LREM从worker-q中删除一个实例,并循环回第1行。

真正的关键是,如果进程在第6行操作期间死亡,我们仍然在worker-q中有这个作业。在重新启动进程时,我们将立即清除第7行中尚未删除的所有作业。这种模式大大降低了失业的可能性。然而,一个作业有可能被处理两次,但只有当worker在第2行和第3行之间或者第5行和第6行之间死亡时,这是不可能的,但最好在worker逻辑中考虑这种情况。

Baidu