基本点
RabbitMQ是一种消息队列。用于解决如下问题:
- 模块与模块之间的耦合度过高问题。
- 通讯成本过高问题
它严格遵循AMQP协议。
其简单架构由如下
- 生产者:发布消息到exchange
- 交换机(exchange):和生产者建立连接并接受消息
- 路由(route):以一定的策略将exchange中的消息发送到queue
- 队列(queue):exchange会将消息根据路由策略分到指定的queue中,并与消费者交互
- 消费者:消费消息
完整架构(图源于网络资料截取)
一个队列中的消息,只会被一个消费者消费一次。
消息不会被重复消费。
安装
利用docker安装rabbitmq非常简单
version: "3.1"
services:
rabbitmq:
image: daocloud.io/library/rabbitmq:management
restart: always
ports:
- 5672:5672
- 15672:15672
volumes:
- ./data:/var/lib/rabbitmq
5672为rabbitmq默认的服务端口号
15672为其图形化管理界面的端口号。
通讯方式
1. hello world型
一个生产者,一个默认交换机,一个队列,一个消费者。
2. work queues型
一个生产者,一个默认交换机,一个队列,两个消费者
3.publish/subscribe 型
一个生产者 一个自定义交换机,多个队列,多个消费者
exchange的类型
- fanout:pubsub 型指定
- direct: routing 型指定
- topic : topic类型指定
4. routing 型
一个生产者,一个交换机,多个队列,多个消费者
消息通过routing-key进行绑定
exchange类型为direct
5. topic 型
一个生产者,一个交换机,多个队列,多个消费者
exchange类型为topic
绑定配对方式
* 为占位符
# 为通配符
举例子:
一个消息现在有 年龄,性别,职位 三种类型
假定有
18,男,教师
20,男,程序员
19, 女,明星
*.男.* 说明只要是男性都可以接收
19.# 说明只要是19岁都可以接收
一个exchange可以绑定多个队列,并且一个队列可以被多次绑定
常见问题
为什么要手动ACK
因为消息只会被消费一次。
如果在程序中,你刚接到消息,然后就自动ack了,后续程序中出现了错误,逻辑没有成果走完,那这条消息相当于就没有作用。
开启手动ack,在代码成功执行后,再ack,可以保证消息的有效利用。
rabbitMQ的可靠性怎么实现
情况一
消息到达了mq,但mq宕机了,消息会丢失吗?
- 消息不会丢失,因为队列有持久化机制。
情况二
消费者受到消息,但执行一半,消费者出现问题,消息会丢失吗?
- 不会,因为可以手动ack,确保消息一定会被有效使用
情况三
生产者发送消息时,没有发送到MQ,这时候怎么办?
- rabbitMQ提供了事务机制和confirm机制
- 事务机制可以保证消息的传递,可以通过回滚去记录日志,然后之后定时再发送,但事务操作效率很低,所以不推荐使用
- confirm机制有三种形式:1. 普通confirm 2. 批量confirm 3. 异步confirm
- 即exchange收到消息后会进行回调。
- 所以confirm机制是保证了消息从生产者到exchange的过程中不出现错误。
情况四
如果消息从exchange发送到queue失败,怎么办?
- return机制
- return机制保证了消息从exchange发送到queue的准确性,如果期间出现问题,程序中可以进行回调提示。
如何避免消息的重复消费
假定现在,有两个服务,服务A消费了消息,但没有ack;然后服务B又消费了此消息,这时候就出现了消息重复消费的情况,怎么解决?
- 用redis解决
- 给消息加上一个id,存入redis。在消息消费之前,将id放入redis中,并将其值标识为0,表达正在执行,消息消费成功后,将值改变为1,标识消费成功。
- 那么其他服务在消息时,先执行setnx,如果key存在,拿值,如果为0,什么都不做,因为其他服务可能正在消费;如果为1,则直接ack,并且删除此key。
- 但可能服务中出现死锁情况,导致值一直为0;可以通过设置key的生存时间解决。