最近用 redis 的 pub/sub 做消息队列,开启多个 MQ 应用进程,由于使用的是发布订阅者模式,多个监听者(消费者)如何保证只有一个监听器接收到消息并消费?
1
evanmeng 2015-12-25 11:08:58 +08:00 1
这不就是要做 Exactly Once 吗?虽然有很多绕过去的做法,但直接的回答就是一句:不能。
|
2
surfire91 2015-12-25 11:11:13 +08:00 1
考虑过走队列吗?哪个消费者拿到了,哪个消费者处理。
|
4
Lullaby OP @surfire91 就是走队列,只是借用了 redis 的 pub / sub ,生产消费者模式能保证只有一个消费者拿到并消费
|
5
evanmeng 2015-12-25 11:19:10 +08:00
在生产者和消费者都有可能当机的现实网络,要保证无数据丢失的 Exactly Once 是做不到的。
常用的做法无非是:用一个 Zookeeper 之类的分布式锁记录已发送信息, pub 要 sub 发送确认,在多个 sub 一方去重。 @Lullaby |
6
est 2015-12-25 11:21:21 +08:00 8
@mathiasverraes
There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery https://twitter.com/mathiasverraes/status/632260618599403520 |
7
odirus 2015-12-25 11:21:57 +08:00 1
无法保证的,建行当年花那么多钱都无法保证,还是需要去重,别去踩坑了。
|
8
yangmls 2015-12-25 11:25:30 +08:00 1
为什么不用 redis 的 lpop ?
|
11
moe3000 2015-12-25 11:57:45 +08:00 1
不要用 pub/sub 用 list 操作
|
12
xiamx 2015-12-25 12:02:06 +08:00 1
无法保证, CAP Theorem: https://en.wikipedia.org/wiki/CAP_theorem
|
13
love 2015-12-25 12:02:16 +08:00 2
只能绕过这个问题。
比如以前我们的做法: 队列向一个 consumer 发一个消息后,把这个消息设置为正在处理,并设置一个倒计时,时间到后 consumer 没有回发确认处理完毕的消息的话再放回队列以便给别的 consumer 再处理。 |
15
surfire91 2015-12-25 12:12:37 +08:00
@Lullaby 我懂了,你是用 pub/sub 来实现消息队列。 pub/sub 就没法保证只消费一次,还不如用 LIST 。
|
16
xufang 2015-12-25 12:22:50 +08:00 via Android 1
简单一点用数据库, sql 的 acid 特性可以保证。 单点问题可以通过主从来实现。
复杂一些上 zk 或 etcd ,这个前面有人提到了。 |
18
zacard 2015-12-25 12:59:20 +08:00 1
一个订阅者即可啊。
|
19
SparkMan 2015-12-25 12:59:57 +08:00 1
还是使用生产者消费者模式吧,否则就算加锁来保证,效率也大大降低了
|
20
eimsteim 2015-12-25 14:00:59 +08:00 1
不开启多个 MQ 应该能解决这个问题吧?如果单个 MQ 没有性能瓶颈的话,就不要开启多个了撒
|
21
Lullaby OP |
22
defage 2015-12-25 14:18:20 +08:00 1
你这是订阅模式,跟 broadcast 一样, 如果在 rabbitmq 中就是 fanout
要做的比较多的策略, 还是用 rabbitmq 这种消息队列服务把 |
23
pokolovsky 2015-12-25 14:41:42 +08:00 1
量子信息能保证只能消费一次。
|
24
ybdhjeak 2015-12-25 14:46:42 +08:00 1
pub/sub 不合适用作队列,在没有订阅者的频道 pub 时会失败,如果要求强实时的话,加快 RPOP 的频率就行, redis 的操作都是原子的
|
25
xujif 2015-12-25 14:51:51 +08:00 1
这个适合用队列
|
26
dingyaguang117 2015-12-25 14:52:33 +08:00 via iPhone 1
使用姿势不对,应该用 list 的 bpop
|
27
Lullaby OP |
28
haogefeifei 2015-12-25 15:09:05 +08:00 1
java 用 synchronized 可以搞定
|
29
est 2015-12-25 15:52:17 +08:00
@pokolovsky 没法保证的。首先你如何检测量子丢包?
|
31
julyclyde 2015-12-26 09:55:22 +08:00 1
@love 说的那种方法,可以通过简单的把 mq 替换为 beanstalkd 来实现
不过还那句话:考虑到 sub 死的时候都喊不出来,这事不可能完全实现 |
32
sbpcx 2015-12-26 10:10:17 +08:00 1
最近也在撸,用的 list 。 pop 的话可以卡看 rbpop ,但是阻塞需要酌情考虑。
|
33
Muninn 2015-12-26 10:34:42 +08:00 1
不用非得用 redis 啊 那个就只能订阅模式的
用 rabbitmq 之类的专业队列 或者如果是 python 项目 用 celery+redis 就好了 |
34
sbpcx 2015-12-26 11:04:20 +08:00
顺便 搭车一问: rbpop 是阻塞的,如果 queue 比较多,也有好多阻塞的,那会不会造成 redis 的链接过大呢?
现在看到如何监听 redis 队列的时候,主要想到了三种想法: 1 、用 rbpop ,阻塞监听,但是会不会碰到链接等待直至过大。 2 、采用 spring data redis 中的 messageListener 方法,大致看了下他的代码,他好像是用的线程池“轮询”的?是吗? 3 、使用一个线程或者线程池在跑监听(类似于循环跑“轮询”),其他的监听队列注册到这个线程或者线程池上面去,当前者发现消息了,就告知后者,类似于 Actor 模式这种的。 求指导。 |