简述kafka的架构设计
Consumer Group:消费者组,消费者组内每个消费者负责消费不同分区的数据,提高消费能力。逻辑上的订阅者。
Topic:将消息分类,生产者和消费者面向的是同一个Topic
Partition:为了实现扩展性,提升并发能力,一个Topic以多个Partition的方式分布到多个Broker上,每个Partition都是一个有序队列。一个Topic的每个Partition都有若干个副本(Replica),一个leader和若干个Follower。生产者发送数据的对象,以及消费者消费数据的对象,都是Leader。Follower负责实时从leader同步数据,保持和Leader数据的同步。Leader发生故障时,某个Follower会成为新的Leader。副本的作用是备份,partition在一个broker上,而同一个partition的副本存在于不同的broker之间。
消息队列如何保证消息的可靠传输
1.保证消息不多发:生产者不重复生产消息;消费者不重复消费消息
a.首先确保消息不多发,这个不常出现,也比较难控制,如果出现了多发,很大原因是生产者自己的原因,如果要避免出现问题,就需要在消费端做控制
b.避免消费者不重复消费,最保险的机制就是消费者实现幂等性,保证就算重复消费,也不会有问题,通过幂等性,也能解决生产者发送消息的问题。
2.保证消息不丢失
a.生产者发送消息时,确保broker确实收到并且持久化了这条消息,比如RabbitMQ的confim机制,kafka的ack机制都可以确保生产者能正确的将消息发送给broker
b.broker要等待消费者真正确认消费到了消息时才删除消息,这里通常就是消费端ack机制,消费者接收到一条消息后,如果确认没问题了,就可以给broker发送一个ack,broker接收ack后才会删除消息。
如何保证消息不被重复消费
ack机制和offset机制,
假如提交ack失败,就会重复消费。offset也是一样。
保证在消费者端实现幂等性。如何保证?
a.如果是redis,每次都是set请求,所以是天然幂等性
b.生产者发送消息的时候带上一个全局唯一的id,消费者拿到消息以后,先根据这个id去redis查一下是否存在,如果没有就加到redis
c.基于数据库的唯一键
消息队列的优缺点,以及基础的使用场景
优点:解耦、异步、削峰
缺点:
1.增加了系统复杂性、幂等、重复消费、消息丢失等问题的引入
2.系统可用性降低、mq故障会影响系统的可用性
3.一致性,消费端可能失败
场景:
1.日志采集:加入日志量比较大,不希望日志的采集影响业务系统的性能。
2.发布订阅:
死信队列是什么?延时队列是什么?
死信队列也是一个消息队列,它是来存放那些没有成功消费的消息的,通常可以作为消息重试。
延时队列用来存放需要在指定时间被处理的元素的队列,通常可以用来处理一些具有过期性操作的业务,比如十分钟没有之父就取消订单
什么是kafka的rebalance机制
避免rebalance机制是kafka调优的一种重要手段。
rebalance机制: consumer group中的消费者与topic下的partion重新匹配的过程。
何时会产生rebalance:
a.consumer group中的成员个数发生变化(没有什么比较好的办法)
b.consumer消费超时(设置阈值)
c.group订阅的topic个数发生变化(业务低峰期的时候做比较好)
d.group订阅的topic的分区数发生变化(业务低峰期的时候做比较好)
coordinaor:通常partition的leader节点所在的broker,负责监控group中的consumer的存活,consumer维持到coordinator,判断consumer的消费超时
a.coordinator通过心跳返回通知consumer进行rebalance
b.consumer请求coordinator加入组,coordinator选举产生leader consumer
c.leader consumer从coordinator获取所有的consumer,发送syncGroup(分配信息)给到coordinator
d.coordinator通过心跳机制将syncGroup下发给consumer
e.完成rebalance
简述kafka的副本同步机制
LEO:下一条待写入的位置
LogStartOffset:起始位置
LastStableOffset:最后一条已提交数据
FirstUnstableOffset:第一条未提交数据
一个partition对应的ISR中最小的LEO作为分区的HW,consumer最多只能消费到HW所在的位置
leader收到消息后会更新本地的LEO,leader还会维护follwer的LEO即remote LEO,follower发出fetch同步数据请求时(携带自身的LEO)、leader会更新remote LEO,更新分区的HW,然后将数据相应给follower,follower更新自身HW(取响应中的HW和自身的LEO中的较小值),LEO+1
ISR:如果一个follower落后leader不超过某个时间阈值,那么则在ISR中,否则在OSR中。
同步副本时,follower获取leader的LEO和logStartOffset,与本地对比,如果本地的logStartOffset超出了leader的值,则超过这个值的数据删除,再进行同步,如果本地的小于leader的,则直接同步。
kafka中zookeeper的作用
老版本中的zookeeper作用是:
/brokers/ids:临时节点,保存所有的broker节点信息,存储broker的物理地址,版本信息,启动时间,节点名称为brokerID,broker定时发送心跳到zk,如果断开则该brokerID会被删除
/brokers/topics:临时节点,保存broker下所有topic信息,每一个topic节点下包含一个固定的partitions节点,partitions的子节点就是topic的分区,每个分区下保存一个state节点、保存着当前leader分区的和isr的brokerID,state节点有leader创建,若leader宕机该节点会被删除,直到有新的leader选举产生、重新成成state节点。选举根据ISR产生。
/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]:维护消费者和分区的注册关系
/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]:分区消息的消费进度Offset
client通过topic找到topic树下的state节点、获取leader的brokerID,到broker树中找到broker的物理地址,但是client不会直接连接,而不是通过配置的broker获取到zk中的信息。
kafka中高性能的原因
kafka不基于内存,而是硬盘存储,因此消息堆积能力很强。
1顺序读写:利用磁盘的顺序访问速度可以接近内存,kafka的消息都是append操作,partition是有序的,节省了磁盘的寻道时间,同时通过批量操作、节省写入次数,partition物理上分为多个segment存储,方便删除
传统:(不是Kafka使用的方式,操作系统保护系统安全性,不让用户态线程直接操作内核态的东西,比如磁盘,网卡)
读取磁盘文件数据到内核缓冲区
将内存缓冲区的数据copy到用户缓冲区
将用户缓冲区的数据copy到socket的发送缓冲区
将socket发送缓冲区中的数据发送到网卡、进行传输
2零拷贝:(Kafka采用的方式:不处理数据,只传递,所以可以采用零拷贝)
a.直接将内存缓冲区的数据发送到网卡传输
b.使用的是操作系统的指令支持
3分区分段+索引
kafka的message(partition)消息实际上是分布式存储在一个小的segment中的,每次文件操作也是直接操作的segment。为了进一步的查询优化,kafka又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种设计,提升了数据读取效率,也提高了数据操作的并行度。
4.批量压缩
多条消息一起压缩后再进行传输,消费端得到消息以后需要解压缩
5.批量读写
6.直接操作page cache,而不是JVM
kafka不太依赖jvm,数据读写不依赖JVM的堆内存,避免GC耗时以及对象创建耗时。直接使用操作系统的pageCache,如果生产消费速率相当,则直接用pageCache交换数据,不需要经过磁盘IO
kafka消息高可靠解决方案
消息发送可靠:
a.ack:
0,不重试
1,lead写入成功就返回了
-1,等待ISR同步完成再返回
b.unclean.leader.election.enable:false,禁止选举ISR以外的follower为leader
c.tries > 1,重试次数,没有收到ack的情况下要多次重试
d.min.insync.replicas > 1:最小同步副本数,没满足该值前,不提供读写服务。
消费可靠:
a.消费完成后要手工提交offset
b.broker:减少刷盘间隔,即broker向pageCache刷新的间隔减少。
c.事务消息,接收到消息以后先做业务处理再返回commit,保证幂等性,防止重复消费。
kafka吞吐量为什么比rocketMQ高
kafka的生产者采用的是异步发送消息机制,当发送一条消息时,消息并没有发送到broker而是缓存起来,然后直接向业务返回成功,当缓存的消息达到一定数量时再批量发送给broker。这种做法减少了网络IO,从而提高了消息发送的吞吐量,但是如果消息生产者宕机,会导致消息丢失,业务出错,所以理论上kafka利用此机制提高了性能但是降低了可靠性。
kafka的pull和push分别有什么优缺点
1.pull表示消费者主动拉取,可以批量拉取也可以单条拉取,所以pull可以由消费者自己控制,根据自己的消息处理能力来进行控制,但是消费者不能及时知道是否有消息,可能会拉到的消息为空
2.push表示broker主动给消费者推送消息,所以肯定是有消息才会推送,但是消费者不能按照自己的能力来消费消息,推过来多少消息,消费者就得消费多少消息,所以可能会造成网络堵塞,消费者压力过大的问题。
rocketMQ的底层实现原理
RockrtMQ由NameServer集群、Producer集群、Consumer集群、Broker集群组成:
1.broker在启动时向所有的NameService注册,并且保持长连接,每30s发送一次心跳
2.Producer在发送消息的时候从NameServer获取Broker服务器地址,根据负载均衡算法选择一台服务器来发送消息。
3.Consumer消费消息的时候同样从NameServer获取Broker地址,然后主动拉取消息来消费。
kafka reblance机制
kafka如何事务是做什么的?
KAFKA 的事务机制,是 KAFKA 实现端到端有且仅有一次语义(end-to-end EOS)的基础;
KAFKA 的事务机制,涉及到 transactional producer 和 transactional consumer, 两者配合使用,才能实现端到端有且仅有一次的语义(end-to-end EOS);
当然kakfa 的 producer 和 consumer 是解耦的,你也可以使用非 transactional 的 consumer 来消费 transactional producer 生产的消息,但此时就丢失了事务 ACID 的支持;
通过事务机制,KAFKA 可以实现对多个 topic 的多个 partition 的原子性的写入,即处于同一个事务内的所有消息,不管最终需要落地到哪个 topic 的哪个 partition, 最终结果都是要么全部写成功,要么全部写失败(Atomic multi-partition writes);
KAFKA的事务机制,在底层依赖于幂等生产者,幂等生产者是 kafka 事务的必要不充分条件;
事实上,开启 kafka事务时,kafka 会自动开启幂等生产者。
kafka的消息是如何持久化的?
kafka如何保证顺序消费
kafka的零拷贝概述