1. 为什么要有消息中间件
在业务系统中,完成一个业务流程通常会有很多步骤,甚至跨越多个系统,这些步骤之间有相互的信息交互和顺序依赖。为了降低系统之间的强耦合关系,会选用一个中间件系统通信,这也是消息中间件系统的主要功能之一。
- 解耦:通过消息中间件解除相关系统之间的强耦合关系;
- 异步:将很长的业务流程分拆成几个部分,请求异步化
- 削峰:对于秒杀、抢购等系统,瞬时流量非常大,通过消息削峰;
- 高性能:一个业务请求如果采用同步等待,会占用系统一个线程资源,但是大部分的业务请求可以快速相应返回,请求异步化,有助于提高系统的性能。
2. 消息队列通信模式
- 点对点模式:点对点模式通常是基于拉取或者轮询的消息传送模型,特点是发送到队列的消息被一个且只有一个消费者进行处理;
- 发布订阅模式:生产者将消息放入消息队列后,队列会将消息推送给订阅过该类消息的消费者;
3. kafka一些常见知识点总结
1、什么是kafka
Apache Kafka是由Apache开发的一种发布订阅消息系统,它是一个分布式的、分区的和重复的日志服务。kafka是由scala语言编写。
2、kafka有哪些组成
producer: 消息的生产者
broker:一个单独的kafka server就是一个broker ,负责消息的读取和存储,一个broker可以管理多个partition。
Topic:消息主题,可以理解为消息的分类,kafka的数据就保存在topic。在每个broker上都可以创建多个topic。
Partition:Topic的分区,每个topic可以有多个分区,分区的作用是做负载,提高kafka的吞吐量。同一个topic在不同的分区的数据是不重复的,partition的表现形式就是一个一个的文件夹。
Replication:每一个分区都有多个副本,当主分区(Leader)故障时,会选择一个副本作为Leader。kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器。
Message:每一条发送的消息主体。
Consumer:消费者。
Consumer Group:将多个消费者组成一个消费者组,在kafka的设计中同一个分区的数据只能被消费者组中的某一个消费者消费。同一个消费者组的消费者可以消费同一个topic的不同分区的数据,这也是为了提高kafka的吞吐量!
Zookeeper:kafka集群依赖zookeeper来保存集群的的元信息,保证分布式一致性
3、kafka消息是怎样产生的,消息是否有大小限制,消息是否是有序的
kafka的消息都是以(key,value)形式存在,生产者决定数据写到那个partition,每个消息的大小不超过1M。
- producer从集群获取分区的leader;
- producer主动将消息推送到leader;
- leader将消息写入本地文件;
- follower主动从leader拉取消息,写入本地文件后返回ack信息;
- leader确认follower的ack信息后,然后返回消息的ack信息给producer。
对于消息的ack机制,主要有三种:不等待broker的回应,等待leader的回应以及等待leader和所有follower的回应。每个消息在被添加到分区时,都会被分配一个offset,它是此消息在分区中的唯一编号,kafka通过offset保证消息在分区内的顺序,offset顺序不跨分区,即kafka只保证在同一个分区内的消息是有序的,在不同的分区中,消息可能是无序的。
图片来自:kafka - 搜索结果 - 知乎
4、kafka的消息是如何存储的
kafka消息以固定格式存储在磁盘上.log文件中:消息由一个固定长度的头部和可变长度的字节数组组成。头部包含了一个版本号和CRC32校验码。消息主要包含消息体、消息大小、offset、压缩类型……等等。offset是一个8byte的有序id号,可以唯一确定消息在parition内的位置;消息大小占用4byte;
kafka在broker上一个topic的partition是一个文件夹,每个partition文件夹下有多个segment(xxx.index,xxx.log,xxx.timeindex),segment文件的大小可以通过配置设置,默认为1G。大小大于1g时,会滚动一个新的segment并且以上一个segment最后一条消息的偏移量命名。kafka就是利用分段+索引的方式来解决查找效率的问题。
5、为了避免磁盘占满,kafka会周期性的清除过期消息,有什么策略,什么是日志压缩
kafka会根据策略定期清除过期消息,策略包括:根据消息的保存时间和根据topic存储数据的大小。
日志压缩是:在很多场景下,消息的key与value对应关系是不断变化的,消费者只关心key对应的最新value,此时,可以开启kafka的日志压缩功能,kafka会在后台启动一个线程,定期将相同key的消息进行合并,只保留最新的value值。
6、kafka如何快速查找到消息
- 根据消息的offset,找到message所在的segment文件(利用二分法查找)
- 找到segment中的.index文件,由于该文件采用的是稀疏索引的方式存储着相对offset及对应message物理偏移量的关系,所以直接找相对offset的索引可能找不到,同样利用二分法查找相对offset小于或者等于指定的相对offset的索引条目中最大的那个相对offset;
- 找到.index文件中的物理位置,然后打开.log文件从物理位置顺序扫描,直到找到Message。
7、kafka消息怎样消费,是push还是pull,消费组的消息能不能重复消费,怎样扩展消费组
消费者会记录消息消费的物理偏移量位置(offset),下一次消费的时候,会接着上一次的位置继续消费。consumer采用pull模式拉取消息,好处是可以根据consumer的消费能力决定消息消费速度,但缺点是如果broker没有消息,会不断轮询,直到新的消息产生,所以kafka可以通过设置让consumer阻塞。kafka支持多个consumer组成一个consumer group,在一个group中,每个partition只会被分配到一个consumer,如果组中members太多,可能会有member空闲。
8、kafka创建topic时,怎么将分区放到不同的broker,新建的分区会在哪个目录下创建
创建topic时,第一个分区的第一个副本,会从brokerlist中随机选取一个broker;其它分区的第一个副本,会在紧随第一个分区的broker后。剩余的副本则相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的。
分区文件夹的创建,会根据启动kafka时配置的log.dirs参数决定。如果log.dirs值配置了一个参数目录,则分配到broker上的partition也会在这个目录下创建。如果log.dirs配置了多个目录,Kafka 会在含有分区目录最少的文件夹中创建新的分区目录,分区目录名为 Topic名+分区ID。
9、什么是ISR,由谁维护,怎么维护
ISR(In-Sync Replica)中的副本都是与Leader进行同步的副本,所以不在该列表的follower会被认为与Leader是不同步的。可以明确的是:Leader副本总是存在于ISR中,而follower副本是否在ISR中,取决于该follower副本是否与Leader副本保持了“同步”。每个分区都有一个同步副本列表,该列表由Leader分区和Controller进行更新。选择一个同步副本列表中的分区作为leader 分区的过程称为clean leader election,在非同步副本中选一个分区作为leader的过程称之为unclean leader election。
kafka写请求先交给leader处理,follower副本会从leader那里拉取写入的消息。这个过程可能会有一定的延迟,导致followers的副本消息落后于leader,如果超过阈值后,follower会从ISR中移除到OSR中。通常情况下,ISR应该包含所有的副本,当ISR中所有Replica都向Leader发送ACK时,leader才commit。不幸的是,unclean leader election可能会造成数据的不一致,这受到两个参数的影响:
- replica.lag.time.max.ms:同步副本滞后与leader副本的时间
- zookeeper.session.timeout.ms:与zookeeper会话超时时间
10、kafka的controller broker
Controller是kafka的核心组件,主要作用是在zookeeper帮助下管理集群,集群中任意一台 Broker 都能充当控制器的角色,但在运行过程中只能有一个 Broker 成为控制器。Broker 在启动时,会尝试去 ZooKeeper 中创建 /controller 节点,第一个成功创建 /controller 节点的 Broker 会被指定为控制器。Controller Broker的主要职责包括以下几个方面:
- 创建、删除主题,增加分区并分配leader分区
- 集群Broker管理(新增 Broker、Broker 主动关闭、Broker 故障)
- preferred leader选举
- 分区重分配
每个 Broker 启动后,会在zookeeper的 /Brokers/ids 下创建一个临时 znode。当 Broker 宕机或主动关闭后,该 Broker 与 ZooKeeper 的会话结束,这个 znode 会被自动删除。ZooKeeper Watch 机制将这一变更推送给控制器,控制器就能知道有 Broker 关闭或宕机了,从而进行后续的协调操作。
Kafka认为leader分区副本最初的分配(每个节点都处于活跃状态)是均衡的(均匀分配在不同机器),这些被最初选中的分区副本就是所谓的首选领导者(preferred leaders)。默认情况下auto.leader.rebalance.enabled为true,表示允许 Kafka 定期地对一些 Topic 分区进行
Leader 重选举。当Controller注意到Broker已加入集群时,它将使用Broker ID来检查该Broker上是否存在分区,如果存在,则Controller通知新加入的Broker和现有的Broker,新的Broker上面的follower分区再次开始复制现有leader分区的消息。为了保证负载均衡,Controller会将新加入的Broker上的follower分区选举为leader分区。
11、kafka的leader重选举过程
kafka副本以topic的分区为单位,正常情况下每个分区都有一个单独的leader,0个或多个follower,副本的总数包括leader。所有的读取和写入到该分区的leader,leader均匀分布在broker。kafka通过将Leader副本均匀地分布在集群的不同Broker上,保障集群的负载均衡。
对于kafka的节点活着有2个条件:一个节点必须能维持与zookeeper的会话;如果它是一个slave,它必须复制leader并且不能落后"太多"。如果一个follower死掉,卡住,或落后,leader将从同步副本列表中移除它。当leader故障后,需要从follower中选出新的leader,kafka优先从ISR中的副本选取leader。选择方式是通过controller选举leader,controller会将Leader的改变直接通过RPC的方式(比ZooKeeper Queue的方式更高效)通知需为此作为响应的Broker。具体选举过程如下:
- 从Zookeeper中读取当前分区的所有ISR(in-sync replicas)集合
- 调用配置的分区选择算法选择分区的leader。
- NoOpLeaderSelector: 不做任何变化
- ControlledShutdownLeaderSelector:找出已经分配的副本,过滤出仍存活的副本集合(liveAssignedReplicas),在该列表中选取第一个broker作为该分区的leader。
- PreferredReplicaPartitionLeaderSelector:即将AR里的第一个取出,作为优化后的leader。
- ReassignedPartitionLeaderSelector:在某个topic重新分配分区的时候,触发新的主副本选举,将存活的ISR中的第一个副本选举成为eader。
- OfflinePartitionLeaderSelector:如果至少有一个broker在ISR列表中,并且存活,则将其选为leader;如果ISR列表为空,且unclean.leader.election.enable=false,则报错NoReplicaOnlineException;如果unclean.leader.election.enable=true,即在AR列表中选出Leader,但是这样会引起数据不一致;若AR列表也为空,则报错NoReplicaOnlineException
在Broker发生故障时,某些Broker上的分区副本会被选举为leader,会造成一个Broker上存在多个leader分区副本的情况,由于客户端只与leader分区副本交互,所以这会给Broker增加额外的负担,并损害集群的性能和运行状况。
11、data replication 如何处理Replica恢复,如何处理replica的全部宕机
如果leader挂掉了,会从replica中选取一个做为新的leader,并且从ISR中移除。如果所有的副本都宕机后,有两种策略:
- 等待ISR中的任意一个replica恢复,并选举为leader;如果ISR中一个也不能恢复,该Partition永久不可用;
- 选取第一个回复的replica作为leader,不论是不是在ISR中。
12、消费者rebalance
消费者实现rebalance是通过coordinator实现的。
- 什么是coordinator: 每个consumer group都会选择一个broker作为自己的coordinator,负责监控这个消费组里的各个消费者的心跳,以及判断是否宕机,然后开启rebalance;
- 如何选择coordinator机器:首先对groupId进行hash(数字),接着对__consumer_offsets的分区数量取模,默认是50,_consumer_offsets的分区数可以通过offsets.topic.num.partitions来设置,找到分区以后,这个分区所在的broker机器就是coordinator机器。比如说:groupId,“myconsumer_group” -> hash值(数字)-> 对50取模 -> 8 __consumer_offsets 这个主题的8号分区在哪台broker上面,那一台就是coordinator 就知道这个consumer group下的所有的消费者提交offset的时候是往哪个分区去提交offset;
-
coordinator Rebalance过程如下:
- 1)每个consumer都发送JoinGroup请求到Coordinator;
- 2)然后Coordinator从一个consumer group中选择一个consumer作为leader;
- 3)把consumer group情况发送给这个leader;
- 4)接着这个leader会负责制定消费方案,通过SyncGroup发给Coordinator ;
- 5)接着Coordinator就把消费方案下发给各个consumer,他们会从指定的分区的 leader broker开始进行socket连接以及消费消息;
-
有三种rebalance的策略:range、round-robin、sticky
13、kafka如何保证消息不丢失,不重复消费
消息丢失从生产端和消费端分析。生产端:
- 1) 使用同步模式时,有3种状态保证消息被安全生产,在配置为1(只保证写入leader成功的话),如果刚好leader partition挂了,数据就会丢失;
- 2)使用异步模式的时候,当缓存区满了,如果配置为0(还没收到确认的情况下,缓冲池一满,就清空缓冲池里的消息),数据就会被立马丢弃掉;
消费端:consumer从集群中拉取到到消息后,消息还没有消费成功,自动提交了offset;
对于重复消费,可以对每一个消费的消息在外部加一个去重处理,或者幂等处理。
14、kafka脑裂
当controller broker挂掉后,Kafka集群必须找到可以替代的controller。但是当集群选举出新的controller后,原来的controller有恢复过来,这个时候就会有两个controller,这种情况称之为脑裂。比如,原来的controller由于GC而被认为已经挂掉。Kafka是通过使用epoch number(纪元编号)来解决脑裂问题的。epoch number是单调递增的数字,第次选出Controller后,epoch number加1。其他Broker 如果收到由controller发出的包含较旧(较小)epoch number的消息,就会忽略它们,即Broker根据最大的epoch number来区分当前最新的controller。
15、kafka linux sendfile技术 — 零拷贝
kafka服务去os cache缓存读取数据,缓存没有就去磁盘读取数据;os cache复制数据到kafka应用程序中,并不会再次复制到用户空间,而是将数据(复制)发送到socket cache中;socket cache通过网卡传输给消费者。
16、kafka选举过程汇总
控制器的选举:broker启动的时候,通过向zookeeper注册临时节点,第一个注册成功的broker成为controller;
分区leader选举:通过controller从ISR/副本中选取,一般默认选取存活列表中的第一个;
GroupCoordinator选举:负责执行消费者的分区分配和再均衡操作,通过消费组的groupId的哈希值计算__consumer_offsets中的分区编号,找到分区后,再寻找分区leader副本所在的broker节点,该节点就是对应的GroupCoordinator。
消费组leader选举:当消费组内还没有leader,第一个加入消费组的消费者即为消费组的leader,如果当前leader退出消费组,则会挑选以HashMap结构保存的消费者节点数据中,第一个键值对来作为leader。
17、kafka的事务Transaction
Kafka 在 0.11版本引入事务支持,生产和消费可以跨分区和会话,要么全部成功,要么全部失败。为了管理 Transaction
,Kafka 引入了一个新的组件 Transaction Coordinator
。Producer 通过和 Transaction Coordinator 交互获得 Transaction ID 对应的任务状态。Transaction Coordinator 还负责将事务所有写入 Kafka 的一个内部 Topic
,这样即使服务重启,进行中的事务状态可以得到恢复。
consumer事务性较弱,主要是 Consumer 可以通过 offset 访问任意信息,而且不同的Segment File 生命周期不同,同一事务的消息可能会出现重启后被删除的情况。
参考链接:https://zhuanlan.zhihu.com/p/446774729
参考链接:https://www.orchome.com/22