一、rabbitmq的应用场合
采用 AMQP 高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦,常见的应用场景:
- 异步处理 先处理主要的业务,异步处理次要的业务,如主要流程是生成订单、扣减库存;次要流程比如购买成功之后会给用户发优惠券,增加用户的积分
- 应用解耦 场景: 以电商的下订单为例子,把消息队列作为中间件,当订单系统下完单后,把数据消息写入消息队列中,库存系统和发货系统同时订阅这个消息队列
- 流量削峰:削去秒杀场景下的峰值写流量
- 日志处理
- 延时任务
二、如何处理消息队列中里积压问题
- 先修复消费端的问题,确保恢复消费速度。要临时扩容,增加消费端的数量,与此同时,降级一些非核心的业务
- 排查解决异常问题,如通过监控,日志等手段分析是否消费端的业务逻辑代码出现了问题,优化消费端的业务处理逻辑。
- 如果是消费端的处理能力不足,可以通过水平扩容来提供消费端的并发处理能力,在扩容消费者的实例数的同时,必须同步扩容主题 Topic 的分区数量,确保消费者的实例数和分区数相等。如果消费者的实例数超过了分区数,由于分区是单线程消费,所以这样的扩容就没有效果。
如何临时扩容
- 新建topic 、partition是原来的10倍,临时新建好原来10倍的queue
- 然后写一个临时的分发数据的程序,这个程序部署上去消费积压的数据,消费之后不做耗时处理,直接均匀轮训写入新建好的queue
- 临时征用10倍的机器来部署消费,每一批consumer消费一个临时queue的数据
- 等快速消费完积压数据之后,得恢复原来的架构
三、如何确保消息不会丢失
- 消息生产阶段: 从消息被生产出来,然后提交给 MQ 的过程中,只要能正常收到 MQ Broker 的 ack 确认响应,就表示发送成功。
- 消息存储阶段:通过分区多副本保证消息不会丢失
- 消息消费阶段:消费端从 Broker 上拉取消息,只要消费端在收到消息后,不立即发送消费确认给 Broker,而是等到执行完业务逻辑后,再发送消费确认,也能保证消息的不丢失。
需要一种机制,来 Check 消息是否丢失了。 在生产端发送消息之前,通过拦截器将消息版本号注入消息中(版本号可以采用连续递增的 ID 生成,也可以通过分布式全局唯一 ID生成)。然后在消费端收到消息后,再通过拦截器检测版本号的连续性或消费状态,
四、如何保证消息不会重复消费
让每个消息携带一个全局的唯一ID,即可保证消息的幂等性,具体消费过程为:
消费者获取到消息后先根据id去查询库中查询是否存在该消息
如果不存在,则正常消费,消费完毕后把该消息写入数据库
如果存在,则证明消息被消费过,直接丢弃
五、说说项目中为什么选择rabbitmq,而是其他消息中间键
rabbitmq消息中间键
- 轻量级,快速,部署使用方便
- 支持灵活的路由配置。RabbitMQ中,在生产者和队列之间有一个交换器模块。根据配置的路由规则,生产者发送的消息可以发送到不同的队列中。路由规则很灵活,还可以自己实现。
- 但是RabbitMQ相对的延迟确实最好的。RabbitMQ的性能在Kafka和RocketMQ中是最差的
RocketMQ是一个开源的消息队列,使用java实现。借鉴了Kafka的设计并做了很多改进,RocketMQ主要用于有序,事务,流计算,消息推送,日志流处理,binlog分发等场景,但跟周边系统的整合和兼容不是很好
Kafka的可靠性,稳定性和功能特性基本满足大多数的应用场景,但是由于是异步的和批处理的,延迟也会高,不适合电商场景
六、死信队列和延迟队列
死信队列,绑定死信交换机的队列就称之为死信队列,当消息成为死信消息后,如果配置了死信队列消息,那么该消息就会被丢进死信队列中,否则被丢弃
延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
七、消息成为死信的三种情况,如何设置超时属性
- 消息被消费者reject或者返回nack。
- 消息超时未消费
- 消息队列中的消息已经超过最大队列长度。
如何设置超时属性:
- 给队列设置ttl属性,进入队列后超过ttl时间的消息变为死信
- 给消息设置ttl属性,队列接收到消息超过ttl时间后变为死信
- 两者共存时,以时间短的ttl为准
八、rabbitmq有几种广播类型
- direct :路由模式,发送方把消息发送到订阅方,如果有多个订阅者,默认采用轮询的方式发送消息
- fanout 广播模式,把消息发给所有的订阅者
- topic 区配订阅模式,使用正则区配到消息队列,能匹配到的都能接收到消息
九、rabbitmq重要的组件
channel(信道) :消息推送使用的通道
exchange(交换机) :用于接收分配消息
queue(队列) :用于存储生产者的消息
routingKey(路由键):用于把生产者的数据分配到交换机上
bindingKey(绑定键):用于把交换机的消息绑定到队列里
十、如何保证RabbitMQ的⾼可⽤?
RabbitMQ对于⾼可⽤是基于主从的⽅式进⾏实现. 其有三种⼯作模式: 单机模式、普通集群模式、镜像集群模式。
单机模式:⽣产环境不适⽤。
普通集群模式:在多个服务器上部署多个MQ实例,每台机器⼀个实例,创建的每⼀个queue,只会存在⼀个MQ实例上. 但是每⼀个实例都会同步queue的元数据(即queue的标识信息). 当在进⾏消费的时候, 就算连接到了其他的MQ实例上, 其也会根据内部的queue的元数据,从该queue所在实例上拉取数据过来.这种模式性能开销巨⼤.容易造成单实例的性能瓶颈. 并且如果真正有数据的那个queue的实例宕机了. 那么其他的实例就⽆法进⾏数据的拉取.
镜像集群模式:这种模式才是⾼可⽤模式. 与普通集群模式的主要区别在于. ⽆论queue的元数据还是queue中的消息都会同时存在与多个实例上.要开启镜像集群模式,需要在后台新增镜像集群模式策略. 即要求数据同步到所有的节点.也可以指定同步到指定数量的节点.缺点:1.性能开销⼤,2.⽆法线性扩容。
13.RabbitMQ的消息是如何路由的?
消息路由有三部分组成:交换器,路由和绑定;⽣产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。
1.消息发布到交换器时,消息将拥有⼀个路由键(routing key),在消息创建时设定。
2.通过队列路由键,可以把队列绑定到交换器上。
3.消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进⾏匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进⼊ “⿊洞”。