消息队列

MQ有什么用?有哪些具体的使用场景?

队列是一种FIFO先进先出的数据结构,而MQ(Message Queue)即消息队列消息,主要作用就是由生产者发送到MQ进行排队,然后由消费者对消息进行处理,具体使用场景如下:

  • 异步:作用能提高系统的响应速度和吞吐量。
  • 解耦:服务之间解耦,可以减少服务之间的影响,提高系统的稳定性和可扩展性,另外,解耦之后可以实现数据分发,生产者发送一个消息后,可以由多个消费者来处理。
  • 削峰:以稳定的系统资源应对突发的流量冲击。

不过,MQ也有一些缺点:

  • 系统的可用性降低:一旦MQ宕机,整个服务就会产生影响。
  • 系统的复杂度提高:引入MQ之后,数据链路就会变得很复杂,并伴随着很多的问题,例如如何保证消息不丢失?消息不会重复调用?怎么保证消息的顺序性?等等
  • 数据一致性:A系统发消息,需要B、C两个系统一同处理。如果B系统处理成功,C系统处理失败,这就会造成数据一致性的问题。

如何进行产品选型?

KafkaRabbitMQRocketMQ
优点吞吐量非常大,性能非常好,集群高可用消息可靠性高,功能全面高吞吐,高性能,高可用,功能非常全面
缺点会丢数据,功能比较单一吞吐量比较低,消息积累会严重影响性能开源版功能不如云上商业版。官方文档和周边生态还不够成熟。客户端只支持Java
适用场景日志分析、大数据采集小规模场景几乎是全场景

如何保证消息不丢失?

这个问题主要分为两个方面,第一,哪些环节会造成消息丢失?第二,在这些可能会造成消息丢失的场景下,如何保证不丢失。

总的来说,消息传递过程中如果存在跨网络的请求,或者由IO操作,就有可能会造成消息丢失,具体如下图:

img

那么如何保证消息不丢失呢?需要按照上面不同场景来单独处理

  1. 生产者发送消息不丢失
产品类型保证生产者发送消息不丢失策略
kafka消息发送+回调
RocketMQ事务消息
RabbitMQ消息发送+回调
手动事务:Channel:txSelect()开启事务,Channel.txCommit()提交事务,Channel.txRollback()回滚事务,这种方式对channel是会产生阻塞的,造成吞吐量下降
publisher confirms。整个处理流程跟RocketMQ的事务消息,基本是一样的。

具体见下图:

img

  1. MQ主从消息同步不丢失
产品类型主从消息不丢失策略
RoctMQ在普通集群中,同步同步、异步同步。异步同步效率更高,但是有丢消息的风险,同步同步就不会丢消息
Rabbit MQ普通集群:消息是分散存储的,节点之间不会主动进行消息同步,是有可能丢失消息的
镜像集群:镜像集群会在节点之间主动进行数据同步,这样数据安全性得到提高。
Kafka通常都是用在允许消息少量丢失的场景,可以通过参数配置:acks:0,1,all
  1. MQ消息存盘不丢失
  • RocketMQ:同步刷盘、异步刷盘:异步刷盘效率更高,但是有可能丢消息,同步刷盘消息安全性更高,但是效率会降低。

  • RabbitMQ:将队列配置成持久化队列

  1. 消费者消费消息不丢失
  • RocketMQ:使用默认的方式消费就行,不要采用异步方式

  • RabbitMQ:autoCommit

  • Kafka:手动提交offset

如何保证消费幂等性?

其实就是要防止消费者重复消费的问题。

所有MQ产品都没有提供主动解决幂等性的机制,需要由消费者自行控制。

RocketMQ:给每个消息分配了MessagesID,这个MessagesID可以作为消费者判断幂等的依据,这种方式不太建议。

最好的方式就是自己带一个有业务标识的id,来进行幂等判断,例如在订单中OrderID

还可以统一ID分配。

MQ如何保证消息顺序?

消息的顺序分为全局有序和局部有序,通常来说,MQ只需要保证局部有序,不需要保证全局有序。

img

Java当中对零拷贝进行了封装,Mmap方式通过MappedByteBuffer对象进行操作,而transfer通过FileChannel来进行操作。

Mmap适合比较小的文件,通常文件大小不要超过1.5G-2G,transfile没有文件大小限制。

RocketMQ当中使用Mmap方式对它的文件进行读写。

在Kafka当中,它的index日志文件也是通过Mmap的方式来读写的。在其他的日志文件当中,并没有使用零拷贝的方式,Kafka使用transfile方式将硬盘数据加载到网卡。

如何保证消息的高效读写?

零拷贝:Kafka和RocketMQ都是通过零拷贝技术来优化文件读写。

传统文件复制方式:需要对文件在内存中进行四次拷贝。

img

零拷贝:有两种方式:mmap和 transfile

img

Java当中对零拷贝进行了封装,Mmap方式通过MappedByteBuffer对象进行操作,而transfer通过FileChannel来进行操作。

Mmap适合比较小的文件,通常文件大小不要超过1.5G-2G,transfile没有文件大小限制。

RocketMQ当中使用Mmap方式对它的文件进行读写。

在Kafka当中,它的index日志文件也是通过Mmap的方式来读写的。在其他的日志文件当中,并没有使用零拷贝的方式,Kafka使用transfile方式将硬盘数据加载到网卡。

MQ 中消费时,业务逻辑出现异常怎么办?

RabbitMQ消息消费失败后的处理方案open in new window

使用MQ如何保证分布式事务的最终一致性?

分布式事务指的是业务相关的多个操作,保证他们同时成功或者同时失败。最终一致性指的是保证事务在最后阶段,能够达到一致性即可,与之对应的就是强一致性。

MQ中要保护事务的最终一致性,就需要做到两点:

  1. 生产者要保证100%的消息投递(使用事务消息机制)
  2. 消费者这一段需要保证幂等消费(唯一ID + 业务自己实现幂等)

分布式MQ的三种语义:at least once、at most once、exactly once

Rocket MQ并不能保证exactly once,商业版本中提供了exactly once的实现机制。

Kafka:在最新版本的饿源码当中,提供了exactly once的demo。

RabbitMQ:使用erlang语言天生就成为了一种屏障

Kafka如何避免重复消费?

详细参见:一文理解Kafka重复消费的原因和解决方案open in new window