问题
最近在写一个功能的时候,由于某些资源需要分布式下每个服务都要执行一次,自然而然就想到了将资源广播出来。由于项目中已经集成了RabbitMq
,本以为一个很简单的问题,结果出现了一些与平常想法不一样的问题
消息的交换器类型以及问题缘由
我们知道RabbitMq
的交换器模式一共分为三种
,分别时direct
, topic
, fanout
, 通过routeKey
将消息路由到绑定到对应交换器的队列上,我们现在要实现广播模式,就直接定义一个为fanout
类型的交换器,由于fanout
模式的路由键无意义,因此我们广播消息的时候直接向这个交换器直接发送消息即可。可问题在于,我定义了一个队列绑定到该交换器上时,最终消息消费时发现并没有如预期的那样,消息被每台机器都消费了,而是无论如何消息只会被某个服务实例消息一次,哎,真让人头大
问题解决
感觉RabbitMq
这个消息中间件,先从概念上让接触消息中间件的人先晕乎一阵子,等慢慢的适应了交换器的概念之后,习惯性的将消息发到交换器然后消费时只关注队列的时候,这个时候广播的实现又把使用者按在地上再摩擦一顿!经过一番测试,得出一个结论,队列中的消息无论如何都会保证只被消费一次,无论你是点对点,还是广播都无法跳出这个定律。但是我现在就想实现广播应该怎么办呢?就是只关注交换器,除了路由键本身就是无效我们不关心以外,队列这个东西我们也不能关心。其实就是消费的时候只指定交换器的名称和交换器的类型为fanout
,然后指定队列名为空串,即不指定定队列即可,让消费者自己去产生队列名称。这样消息产生的时候,广播的交换器有几个消费者就会产生几个队列名称。代码如下。
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "", durable = "true", autoDelete = "false"),
exchange = @Exchange(value = BindingConst.ExchangeName.WS_PROMISE_FANOUT,type = ExchangeTypes.FANOUT)
))
队列名的效果就是下面这个样子滴