消息队列
MQ有什么用?有哪些具体的使用场景?
队列是一种FIFO先进先出的数据结构,而MQ(Message Queue)即消息队列消息,主要作用就是由生产者发送到MQ进行排队,然后由消费者对消息进行处理,具体使用场景如下:
- 异步:作用能提高系统的响应速度和吞吐量。
- 解耦:服务之间解耦,可以减少服务之间的影响,提高系统的稳定性和可扩展性,另外,解耦之后可以实现数据分发,生产者发送一个消息后,可以由多个消费者来处理。
- 削峰:以稳定的系统资源应对突发的流量冲击。
不过,MQ也有一些缺点:
- 系统的可用性降低:一旦MQ宕机,整个服务就会产生影响。
- 系统的复杂度提高:引入MQ之后,数据链路就会变得很复杂,并伴随着很多的问题,例如如何保证消息不丢失?消息不会重复调用?怎么保证消息的顺序性?等等
- 数据一致性:A系统发消息,需要B、C两个系统一同处理。如果B系统处理成功,C系统处理失败,这就会造成数据一致性的问题。
如何进行产品选型?
Kafka | RabbitMQ | RocketMQ | |
---|---|---|---|
优点 | 吞吐量非常大,性能非常好,集群高可用 | 消息可靠性高,功能全面 | 高吞吐,高性能,高可用,功能非常全面 |
缺点 | 会丢数据,功能比较单一 | 吞吐量比较低,消息积累会严重影响性能 | 开源版功能不如云上商业版。官方文档和周边生态还不够成熟。客户端只支持Java |
适用场景 | 日志分析、大数据采集 | 小规模场景 | 几乎是全场景 |
如何保证消息不丢失?
这个问题主要分为两个方面,第一,哪些环节会造成消息丢失?第二,在这些可能会造成消息丢失的场景下,如何保证不丢失。
总的来说,消息传递过程中如果存在跨网络的请求,或者由IO操作,就有可能会造成消息丢失,具体如下图:
那么如何保证消息不丢失呢?需要按照上面不同场景来单独处理
- 生产者发送消息不丢失
产品类型 | 保证生产者发送消息不丢失策略 |
---|---|
kafka | 消息发送+回调 |
RocketMQ | 事务消息 |
RabbitMQ | 消息发送+回调 |
手动事务:Channel:txSelect()开启事务,Channel.txCommit()提交事务,Channel.txRollback()回滚事务,这种方式对channel是会产生阻塞的,造成吞吐量下降 | |
publisher confirms。整个处理流程跟RocketMQ的事务消息,基本是一样的。 |
具体见下图:
- MQ主从消息同步不丢失
产品类型 | 主从消息不丢失策略 |
---|---|
RoctMQ | 在普通集群中,同步同步、异步同步。异步同步效率更高,但是有丢消息的风险,同步同步就不会丢消息 |
Rabbit MQ | 普通集群:消息是分散存储的,节点之间不会主动进行消息同步,是有可能丢失消息的 |
镜像集群:镜像集群会在节点之间主动进行数据同步,这样数据安全性得到提高。 | |
Kafka | 通常都是用在允许消息少量丢失的场景,可以通过参数配置:acks:0,1,all |
- MQ消息存盘不丢失
RocketMQ:同步刷盘、异步刷盘:异步刷盘效率更高,但是有可能丢消息,同步刷盘消息安全性更高,但是效率会降低。
RabbitMQ:将队列配置成持久化队列
- 消费者消费消息不丢失
RocketMQ:使用默认的方式消费就行,不要采用异步方式
RabbitMQ:autoCommit
Kafka:手动提交offset
如何保证消费幂等性?
其实就是要防止消费者重复消费的问题。
所有MQ产品都没有提供主动解决幂等性的机制,需要由消费者自行控制。
RocketMQ:给每个消息分配了MessagesID,这个MessagesID可以作为消费者判断幂等的依据,这种方式不太建议。
最好的方式就是自己带一个有业务标识的id,来进行幂等判断,例如在订单中OrderID
还可以统一ID分配。
MQ如何保证消息顺序?
消息的顺序分为全局有序和局部有序,通常来说,MQ只需要保证局部有序,不需要保证全局有序。
Java当中对零拷贝进行了封装,Mmap方式通过MappedByteBuffer对象进行操作,而transfer通过FileChannel来进行操作。
Mmap适合比较小的文件,通常文件大小不要超过1.5G-2G,transfile没有文件大小限制。
RocketMQ当中使用Mmap方式对它的文件进行读写。
在Kafka当中,它的index日志文件也是通过Mmap的方式来读写的。在其他的日志文件当中,并没有使用零拷贝的方式,Kafka使用transfile方式将硬盘数据加载到网卡。
如何保证消息的高效读写?
零拷贝:Kafka和RocketMQ都是通过零拷贝技术来优化文件读写。
传统文件复制方式:需要对文件在内存中进行四次拷贝。
零拷贝:有两种方式:mmap和 transfile
Java当中对零拷贝进行了封装,Mmap方式通过MappedByteBuffer对象进行操作,而transfer通过FileChannel来进行操作。
Mmap适合比较小的文件,通常文件大小不要超过1.5G-2G,transfile没有文件大小限制。
RocketMQ当中使用Mmap方式对它的文件进行读写。
在Kafka当中,它的index日志文件也是通过Mmap的方式来读写的。在其他的日志文件当中,并没有使用零拷贝的方式,Kafka使用transfile方式将硬盘数据加载到网卡。
MQ 中消费时,业务逻辑出现异常怎么办?
使用MQ如何保证分布式事务的最终一致性?
分布式事务指的是业务相关的多个操作,保证他们同时成功或者同时失败。最终一致性指的是保证事务在最后阶段,能够达到一致性即可,与之对应的就是强一致性。
MQ中要保护事务的最终一致性,就需要做到两点:
- 生产者要保证100%的消息投递(使用事务消息机制)
- 消费者这一段需要保证幂等消费(唯一ID + 业务自己实现幂等)
分布式MQ的三种语义:at least once、at most once、exactly once
Rocket MQ并不能保证exactly once,商业版本中提供了exactly once的实现机制。
Kafka:在最新版本的饿源码当中,提供了exactly once的demo。
RabbitMQ:使用erlang语言天生就成为了一种屏障