如何保证Kafka的可靠性、幂等性和有序性

之前的项目中由于用到了Kafka,所以在前一阵的实习面试中被问到了如何保证Kafka消息的有序性,所以本文对于Kafka这三个常被问到的问题进行总结归纳。

可靠性

对于消息队列而言,如果不能保证消息的可靠性可能会引起重大的生产事故。如果我们在超市里买完东西用手机进行支付,这条支付的消息存进了Kafka,但是因为某些原因导致消息丢失,商家没有收到钱,而我们却已经显示扣费,这是绝对不能容忍的,所以消息队列的可靠性至关重要。

对于可靠性的保障,主要需要从生产者、Broker和消费者三个角度来进行实现,我们先简单介绍一下。

Kafka是基于发布订阅模型的消息队列,主要包括生产者、Broker和消费者三个部分。生产者在Kakfa模型中负责生产消息,并将生产的消息发送到Broker中进行存储,消费者从Broker中拉取订阅的消息进行消费。如下图所示:

如何保证Kafka的可靠性、幂等性和有序性_第1张图片

从上图我们可以得知Kafka可能出现消息丢失的三个地方就是生产者、Broker和消费者。下面我们分别介绍三种情况。

保证生产者的消息可靠性

从本质上来说,生产者与Broker之间是通过网络进行通讯的,因此保障生产者的消息可靠性就必须要保证网络可靠性,这里Kafka使用acks=all可以设置Broker收到消息并同步到所有从节点后给生产者一个确认消息。如果生产者没有收到确认消息就会多次重复向Broker发送消息,保证在生产者与Broker之间的消息可靠性。

保证Broker的消息可靠性

在Broker收到了生产者的消息后,也有可能会丢失消息,最常见的情况是消息到达某个Broker后服务器就宕机了。这里需要补充说明一下Kafka的高可用性,直观的看,Kafka一般可被分成多个Broker节点,而为了增加Kafka的吞吐量,一个topic通常被分为多个partition,每个partition分布在不同的Broker上。如果一个partition丢失就会导致topic内容的部分丢失,因此partition往往需要多个副本,以此来保证高可用。

如何保证Kafka的可靠性、幂等性和有序性_第2张图片

如上图所示,一个topic被分成三个partition,每个partition又进行复制,假如此时Broker1挂了,在Broker2和Broker3上依然有完整的三个partition。此时只需要重新选举出partition的leader即可。这里还是需要强调一下,一定要将leader上的数据同步到follower上才能给生产者返回消息,否则可能造成消息丢失。

保证消费者的消息可靠性

这里比较容易发生消息丢失的情况是,消费者从Broker把消息拉过来,如果这个时候还没有消费完,消费者就挂了并且消费者自动提交了offset,那么此时就丢失了一条消息。所以解决办法就是关闭自动提交offset,等真正消费成功之后再手动提交offset。

幂等性

保证消息的幂等性,其实就是保证消息不会被重复消费。幂等性的保证需要根据具体业务具体分析,比如向MySQL插入一条订单信息,可以根据订单id查询数据库中是否已经存在该信息进行去重。如果是类似于Redis的设置key值,Redis天然支持消息的幂等性,所以这种情况下是不需要关心消息的幂等性的。总之,对于幂等性的保证完全可以根据业务需求进行具体分析。

有序性

消息队列在某些场景下需要严格保证消息被消费的顺序,想象一个场景,你看到你的男朋友在朋友圈晒别的女生的照片,于是你在下面评论了一句:“渣男”,然后把他的微信删了。这时候如果这两条消息的消费顺序是反过来的,也就是先删除微信,然后进行评论,那么是无法评论的。

前面说到可靠性的时候我们提到了Kafka的topic是由多个partition组成的,那么我们可以用一种最极端的方式保证消息有序性,一个topic只设置一个partition。这里的问题就是如果一个topic只对应一个partition,那么这个吞吐量肯定就大幅下降了,这就违背了Kafka的设计初衷。

如何保证Kafka的可靠性、幂等性和有序性_第3张图片

还有一种方法是比较推荐的,由于不同partition之间是不能保证有序性的,因此保证消息在同一个partition中是保证消息有序性的关键,除了前面说的那种极端解决方案,其实还可以在发送消息时,指定一个partition,或者指定一个key,因为同一个key的消息可以保证只发送到同一个partition,这里的key一般可以用类似userid的属性来表示。在上面的场景来看就是妹子的userid先是进行了评论操作,又进行了删除好友的操作,这两个操作由于是同一个key值,因此被发往同一个partition中。

你可能感兴趣的:(消息队列,Kafka,消息队列,中间件)