Kafka是一个成熟的消息队列,是一个天然分布式、支持分区(partition)、多副本(replica)。是基于Zookeeper协调的分布式消息系统。它最大特性就是可以实时的处理大量数据以满足各种需求场景:比如:基于Hadoop的批处理系统,低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等。Kafka使用Scala语言编写的。
Zookeeper用于维护Kafka集群的状态和元数据信息,例如主题和分区的分配信息、消费者组和消费者偏移量等。
关于Zookeeper可以参考我之前的文章了解:
Kafka的架构是整体设计比较简单,是显示的分布式架构,主要由Producer(生产者)、broker(Kafka集群)、和consumer(消费者)组成。
如图所示:
Producer(生产者):生产者负责将消息发布到Kafka集群中的一个或多个Topic(主题)中,每个Topic包含一个或多个Partition(分区)
Topic:主题,是承载消息的逻辑容器,在实际使用中多用来区分具体的业务Partition:分区,一个有序不变的消息序列。每个主题可以有多个分区
Consumer(消费者):消费者负责从Kafka集群中的一个或多个主题消费消息,并将消息的offset(偏移量)提交回Kafka以保证消息的顺序性和一致性。
偏移量:offset,表示分区中每条消息的位置信息,是一个单调递增且不变的值
Kafka集群:Kafka集群是由多个Kafka节点(Broker)组成的分布式系统。每个节点都可以存储一个或多个Topic(主题)的Partiton(分区副本),以提高可用性和容错能力
Leader Broker:Leader Broker是分区的副本,它是负责处理消息读写请求的节点。生产者将消息发送到LeaderBroker,消费者从Leader Broker中拉取消息Follower Broker:Follower Broker是Leader Broker的备份节点,它负责与LeaderBroker进行数据同步,以保持自己的数据与Leader Broker一致在集群中,每个分区都有一个Leader Broker和多个Follower Broker,只有Leader Broker才能处理生产者和消费者的请求,而Follower Broker只是Leader Broker的备份,用于提供数据的冗余备份和容错能力。如果Leader Broker发生故障,Kafka集群将会自动将Follower Broker提升为新的Leader Broker,从而实现高可用性和容错能力
当我们需要自己设计一个MQ的时候也可以从上述比较好的思想中提炼出我们所需要的:
关于如何写一个消息队列,该如何进行架构设计,可参考文章:
场景题-如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。
正常情况下,消息丢失大概分为三种情况:
注意:Kafka只对已提交的消息做最大限度地持久化保证不丢失,但是办法保证100%。后面会讲
消息的生产者,消息发送给Kafka集群的过程中有可能会出现异常失败。所以需要有机制来确保消息能够成功发送。(但是还是存在网络波动的问题无法保证一次消息一定能发送成功)。如果没有从成功需要重新发送知道成功。
我们在使用Kafka发送消息的时候,通常使用的时producer.send(msg)
来发送消息,这是一种异步发送
,发送消息的时候方法会立即返回,但不一定代表消息发送成功了。当时方法prodcuer.send(msg).get()
是同步等待返回的。
所以我们通常为了保证消息在发送不丢失,会建议使用producer.send(msg, callback)
方法,这个方法支持传入一个callback,我们可以在消息发送的时候进行重试。同时Producer还提供了一些配置参数来提升发送成功率:
acks=-1 # 或者 acks=all 该参数表示Leader 和 Follower都接受成功时确认,可以最大限度保证消息不丢失,但是吞吐量低。
retries=3 # 重试次数,也可以设置为max ,一旦失败就会无限重试,卡在这里。
retry.backoff.ms = 300 # 消息发送超时或失败后,间隔的重试时间
acks = 0:表示Producer请求立即返回,不需要等待Leader的任何确认。这种方案有最高的吞吐率,但是不保证消息是否真的发送成功acks = -1:等价于(acks = all),表示分区Leader必须要等待消息被成功写入到所有的ISR副本(同步副本)中才认为Producer请求成功。这种方案提供最高的消息持久性保证,但是理论上吞吐率也是最差的。acks = 1:表示Leader副本必须应答此Producer请求并写入消息到本地日志,之后Producer请求被认为成功。如果此时Leader副本应该请求之后挂掉了,消息会丢失。这个方案,提供了不错的持久性保证和吞吐
在实际业务场景会存在Kafka的Leader接收到了消息,但是还没有来得及同步给Follower就挂掉了,此时Follower变成了Leader,导致数据丢失。
在Kafka集群中有一些机制来保证消息的不丢失,比如:复制机制、持久化存储机制以及ISR机制。
当然在Kafka中还提供了一些配置参数来避免消息丢失的问题:
replication.factor # 表示分区副本数量,replication.factor >1 当Leader副本挂了,Follower副本会被选举为Leader继续提供服务
min.insync.replicas # 表示 ISR 最少副本的数量,通常设置min.insync.replias > 1 这样才能有可用的follower副本执行替换,保证消息不丢失
unclean.leader.election.enable = false # 是否可以把非 ISR 集合中的副本选举为 Leader副本
消费者消费消息的时候,消息还没有处理完成,便自动提交了offset。导致消息没有消费丢失掉。
所以就需要保证不要乱提交offset就行了。在这方面Kafka消费者会跟踪每个分区的offset(偏移量),消费者每次消费消息时,都会将offset向后移动。当消费者宕机或者不可用时,Kafka会将该消费者所消费的分区的offset保存下来,下次该消费者重新启动时,可以从上一次offset重新开始消费
另外,Kafka消费者还可以组成消费者组,每个消费者组可以同时消费多个分区。当一个消费者组中的消费者宕机或者不可用时,其他消费者仍然可以消费该组的分区,保证消息不丢失。
同时也可以关闭自动提交offset,去手动提交offset,避免拉取了消息以后,业务逻辑没处理完,提交偏移量后但是消费者挂了的问题:
enable.auto.commit=false
好了,本章节到此告一段落。希望对你有所帮助,祝学习顺利。
我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。