消息队列-RabbitMQ
1. 消息队列
1.1 优缺点
答:总结为:
优点:
- 解耦。系统生产消息后,直接给MQ不用关心其他事务。
- 异步。异步执行,提高吞吐量。发送者将消息发送给消息队列后,不需要同步等待接收者处理完毕,而是可以进行其它操作。
- 削峰。请求在MQ中,Server根据处理能力处理消息,缓解服务器压力。
缺点:
- 系统可用性降低。MQ挂了,整个系统通信GG。
- 系统复杂度增加。加入MQ,引出一致性、传输可靠性、消息不被重复消费等等问题。
- 一致性问题。A处理结束返回,BC写库成功,D失败,数据不一致。
1.2 消息中间件
答:主要是ActiveMQ、RabbitMQ、RocketMQ和KafKa。
- ActiveMQ:老技术,现在用得不多。
- RabbitMQ:开源、社区活跃,中小型用这个。
- RocketMQ:阿里开发,和Dubbo RPC框架很像。
- Kafaka:专门做大数据。
1.3 MQ常见问题和解决思路
- 消息顺序问题:让消息按发送顺序被消费。
- 解决方法:保证生产者-MQ-消费者,一一对应。合理设计规避。
- 缺陷问题:吞吐量不够;耦合度太高。从业务层面保证消息顺序。
- 消息重复问题:因为网络问题导致消费者收到两条一样的消息。
- 解决方法:保证幂等性,即不管多少重复消息,最后处理结果还是一样。通过唯一编号标识消息或者日志表记录去重。
2. RabbitMQ
RabbitMQ是一款开源的、erLang编写,基于AMQP(Advanced Message Queuing Protocol)的消息中间件。
2.1 基本概念
- Producer(生产者):生产消息的一方(邮件投递者);
- Consumer(消费者):消费消息的一方(邮件收件人);
- Broker:MQ服务器实体,一般认为一个Server就是一个Broker;
- Routing Key:路由键,指定消息的路由规则;
- Exchange:消息交换器,根据路由键把消息分发到队列中;
- Queue:消息队列容器,保存消息直到发送给消费者,每个消息可以投入到一个或多个队列。;
- Binding:绑定,把exchange和queue按照路由规则绑定起来;
- VHost:理解为虚拟broker(mini-RabbitMQ),拥有独立的组件和权限系统,可以做到vhost范围的用户控制,所以一般应用于权限隔离。
- Channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
由Exchange、Queue、RoutingKey三个才能决定一个从Exchange到Queue的唯一的线路。
2.2 工作模式
2.2.1 Simple简单收发
- 生产者生产消息,把消息放进队列;
- 消费者监听队列消息,有就消费掉。
2.2.2 Work竞争资源
- 生产者生产消息,把消息放进队列;
- 多个消费者监听同一队列,竞争消费,谁抢到谁就消费。
- 可能导致重复消费问题。
2.2.3 Publish/Subscribe共享资源
- 生产者将消息发给broker,由交换机转发到队列中;
- 每个消费者监听自己的队列。
2.2.4 routing路由模式
- 生产者将消息发给broker,指定路由规则,交换机根据路由key转发到对于的消息队列;
- 只有对应的消费者才能消费消息。
2.2.5 topic主题模式
- 基于路由模式的模糊匹配,*匹配多个单词,#匹配一个单词(类似sql的模糊查询)
2.3 消息相关问题
2.3.1 消息顺序性
答:通常可以用两种方法:
- 拆分为多个queue,queue和consumer一对一
- queue唯一consumer多个,consumer内部排队分发
2.3.2 消息幂等性
答:解决思路就是,对消息做唯一标识,根据标识判断是否被消费。
2.3.3 消息传输方式
答:RabbitMQ使用信道传输数据,信道基于TCP连接,但不受数量限制,一条TCP连接上信道能无上限。
2.3.4 消息确认模式
答:分为两种确认模式。
- 发送方确认模式
- 消息被分配唯一ID,当消息投递到队列后,信道发送ack给生产者,出错就发送nack。
- 异步,在等待确认过程中仍能继续发消息。
- 接收方确认模式
- 消费者每接收一条消息就确认一次。当消息被确认,才从队列中删除。
- 没有超时机制。只要consumer连接不中断,就认为消费者一直在处理。
2.3.5 消息丢失
答:分为三种情况。
- 生产者丢失消息
- transaction事务机制:把消息的发送作为一个事务,过程中有异常就回滚,发送成功就提交。
- confirm模式:就是发送方消息确认模式。
- 消息队列丢失消息
- 一般都是在开启持久化时发生。
- 和confirm机制配合使用,在消息投递队列并持久化后,再返回ack。
- 消费者丢失消息
- 消费者接收消息后,处理消息前,回复MQ已收到。这时异常会导致消息丢失。
- 修改为手动确认消息。
2.3.6 消息积压
答:处理方法是临时扩容。将queue和consumer资源扩大10倍(申请10倍的空间建立queue和10倍的机器部署consumer),积压消息消费完后,恢复原先架构。
2.3.7 消息失效
答:RabbitMQ能设置消息的TTL,一旦消息积压过久到达TTL就会被自动清理。解决方案是手动批量重导,手动将丢失数据,查出来并发送到MQ中。
2.3.8 延迟队列
消息/队列 TTL + 死信队列 DLX + Router转发队列
设置了TTL后,当消息在队列中变成死信,就被转发到其他队列中。
2.4 集群模式
答:RabbitMQ基于主从模式实现高可用,分为普通集群和镜像集群模式。
- 普通集群模式
- 在多台机器上启动多个RabbitMQ实例。queue放在一个实例A上,但每个实例都同步queue的元数据。
- 消费时,若连接到另一个实例B,则实例B会从实例A上拉取数据。
总结:集群的多个节点服务一个queue的读写操作。
- 镜像集群模式
- 每个RabbitMQ都有一个queue的完整镜像。每次写消息到queue时,自动把消息同步到多个实例上。
- 好处是单点宕机不怕,有备份。坏处是开销大。
2.5 交换器类型
- fanout:把消息不做判断全部放进队列。用来广播信息。
- direct:把消息放进路由完全匹配的队列中。用来处理优先级任务。
- topic:路由模糊匹配。
- header:利用消息的header属性进行匹配。基本不用。