写在前面
近期看到有篇文章说,找到一件可以产生效益的事情,然后持之以恒,迭代的只有完成它的方法,而不需要变更执行它的决心。
我觉得说的很对。
很喜欢士兵突击。当时看时以为我是成才,傻子才是许三多。现在回头想想,才发现能当许三多是很难的事情。
人太聪明了,聪明到会考虑很多,结果忘了最原初的事情。
道理人人都懂,事情人人都能做。但许三多才会去做,才成了兵王。
所以,找到坚持有益的事情,然后就是坚持与提高它的效率。然后就是陪着自己和时间慢慢走。
基础知识
Broker是什么
Broker可以看做是一台Kafka服务器,但其实是运行在一台网络服务器上的进程。它有如下作用:
- 接收Producer写入消息的请求
- 接收Consumer拉取消息的请求
- 负责消息的持久化存储(内存->硬盘)
- 负责Partition的不同Replicas之间的数据同步与故障迁移
Broker的生命周期
- 启动阶段:当Broker启动时,它会加载配置文件,初始化日志管理器,创建网络层和请求处理层,连接到ZooKeeper,并注册自己到集群。如果集群没有Controller,它也会尝试竞选成为Controller。
- 运行阶段:当Broker运行时,它会处理来自生产者和消费者的请求,包括写入和读取数据,以及同步和复制数据。如果Broker是Controller,它还会处理来自其他Broker的请求,包括管理集群的元数据,分配和转移分区,选举和切换Leader等。同时,Broker也会与ZooKeeper保持心跳通信,检测集群的状态和变化。
- 重新选举阶段:当Broker失去与ZooKeeper的连接或者宕机时,它会退出Controller角色,并释放/controller目录的锁。此时,ZooKeeper会通知其他broker开始新一轮的选举。原来的Controller会尝试重新竞选,如果成功,它会恢复到运行阶段;如果失败,它会成为Follower,并等待下一次选举。
- 关闭阶段:当Broker关闭时,它会先停止接收新的请求,并等待已有的请求完成。然后,它会关闭网络层和请求处理层,断开与ZooKeeper的连接,并从集群上注销自己。最后,它会关闭日志管理器,并释放资源。
Broker有哪些配置项
通过配置项可以直接从参数的角度来了解Broker。参数有很多,这里只覆盖了一部分。但能从这些参数来看到Broker有自己服务器维度、网络连接、与ZooKeeper交互、Log存储、消费者与消息多维度的参数设置。
Broker Server基础配置
broker.id:整个Kafka集群内标识唯一Broker的ID。整数类型。这个值其实也是ZooKeeper在/brokers/ids路径下创建的节点ID。若不指定,或者指定为-1,那么ZooKeeper就会从reserved.broker.max.id这个参数(默认1000)加1,用作Broker的IDdelete.topic.enable:是否允许删除Topic。auto.create.topics.enable:是否允许在Producer在未指定Topic发送Message时自动创建Topic。
Broker Socket 配置
listeners:Broker之间,Client与Broker之间通信建立连接时使用的信息。即Broker的监听者,可以以逗号分割配置多个。它的格式为[安全协议]://Hostname/IP:Port。Kafka支持两种传输层协议(PLAINTEXT或SSL)(加密消息)与两种认证层安全协议(SSL或SASL)(认证客户端与服务器身份)。advertised.listeners:将Broker建立通信的地址发布到Zookeeper中,便于Client(Producer和Consumer)连接。它的格式和listener一致。listener.security.protocol.map:以Key/Value的形式定义监听者的安全协议,在大多数情况下会将Key认为是监听者的别名。inter.broker.listener.name:设置内部通信时使用哪个监听者。可以直接设置listener.security.protocol.map中设置的Key。num.network.threads:Broker Server接收请求及发送响应时启用的线程数量。num.io.threads:Broker Server处理请求、对Message进行I/O操作时启用的线程数。
Broker Log 配置
Broker Log存储也是很重要的内容,这里先按下不表。
log.dirs:日志、Message保存的路径。num.partitions:创建Topic时,如果没有指定Partition数量,则使用该配置项设置的Partition数量。num.recovery.threads.per.data.dir:每个数据目录启用几个线程来处理,这里的线程数和数据目录数是乘积关系,并且只在Broker启动或关闭时使用。具体如下:- 当服务器正常启动时,用于打开每个分区的日志片段。
- 当服务器发生崩溃并重启时,用于检查和截断每个分区的日志片段。
- 当服务器正常关闭时,用于关闭日志片段。
min.insync.replicas:当acks=all时,至少有多少个Replicas需要确认已持久化数据,包括Leader。log.retention.hours:Kafka保留Message的时间,默认是168小时,即7天。log.retention.check.interval.ms:检测Message是否可以被删除的时间间隔。log.retention.bytes:已保留的消息的字节总数来判断旧消息是否过期,这个值对应的是每一个分区,如果一个topic有3个分区,这个值设为1GB, 那么该主题最多保留3GB数据。log.segment.bytes:每个Segment文件的大小,默认是1G。当日志大小超过该值时,下一次日志写入就会创建一个新的segment。
Broker ZooKeeper 配置
2.7版本还在使用,之后3.x版本已经不使用了。
zookeeper.connect:设置Zookeeper地址。可用逗号分割配置多个地址,既Zookeeper集群的地址。zookeeper.connection.timeout.ms:等待连接Zookeeper的超时时间。
Consumer Group 配置
group.initial.rebalance.delay.ms:当Consumer Group新增或减少Consumer时,重新分配Topic Partition的延迟时间。
Message 配置
message.max.bytes:Broker接收每条Message的最大值,默认是1M。fetch.message.max.bytes:Consumer每次获取Message的大小。
Broker集群原理是什么?
Kafka可以设置多个Broker来实现分布式架构,提供良好的水平扩展性,可以支持大吞吐量的消息处理能力。而多个Broker通过构成一个集群,那么多个Broker之间是如何协调的?相互如何通信?
如何管理Broker?
Kafka通过Controller与ZooKeeper(2.7版本之前)来实现对Broker的管理。
Broker的启动与下线
当一个Broker启动时,它会做以下这些事情:
- 初始化:加载配置文件、初始化日志管理器等初始化动作;
- 注册:连接到ZooKeeper,并在ZooKeeper的
/brokers/ids路径下创建一个临时节点来注册自己(填入配置的broker.id) - 监听:注册自己之后,还会监听
/brokers/ids这个路径节点,当这个节点下增加或删除子节点时,ZooKeeper 会通知监听了的 Broker。 - 竞选:如果当前集群没有Controller,那么Broker还会尝试竞选Controller。
当一个Broker下线时,它会做以下这些事情:
- 停止:停止接受新的请求,并等待处理中的请求完成;
- 关闭:关闭与网络和请求处理层的连接,断开与ZooKeeper的连接,删除之前注册的节点。关闭日志管理器,释放连接资源。
Controller
Controller是Broker中的Leader,非Controller的Broker就是Follower。他有如下用途:
- 管理和协调Kafka集群的元数据,分区的分配和状态转移,以及处理Broker的故障
- 管理主题的创建、删除、增加分区等操作
- 管理分区的重分配、领导者选举、负载均衡等操作
- 监控集群成员的变化,包括broker的增加或减少、宕机或恢复等
- 向其他broker提供数据服务,包括同步集群元数据、分区信息、副本信息等
Controller的生命周期
- 初始化阶段:当Broker启动时,它会尝试在ZooKeeper的
/controller目录下创建一个临时节点,并写入自己的broker.id,从而竞选成为Controller。如果成功,它会初始化Controller的上下文,包括集群的元数据、分区状态机、副本状态机、事件管理器等。如果失败,它会成为Follower,并监听/controller目录的变化。 - 运行阶段:当Broker成为Controller后,它会在运行阶段处理各种事件,例如Broker的增加或减少、Topic的创建或删除、Partition的分配或转移、Leader的选举或切换等。这些事件会由Controller事件管理器来调度和执行,根据不同的事件类型,触发不同的控制器动作。同时,Controller也会与其他Broker保持心跳通信,检测Broker的可用性和健康状况。
- 重新选举阶段:当Broker失去与ZooKeeper的连接或者宕机时,它会退出Controller角色,并释放/controller目录的锁。此时,ZooKeeper会通知其他Broker开始新一轮的选举。原来的Controller会尝试重新竞选,如果成功,它会恢复到运行阶段;如果失败,它会成为Follower,并等待下一次选举。
- 销毁阶段:当Broker关闭时,它会销毁Controller相关的资源,包括上下文、状态机、事件管理器等,并从ZooKeeper上注销自己。
Controller的选举流程
答案就隐藏在上面的生命周期中。具体来说有如下情况:
- 当集群启动时,每个Broker都会尝试在ZooKeeper的
/controller目录下创建一个临时节点,并写入自己的broker.id; - ZooKeeper保证只有一个Broker能够成功创建
/controller节点,该Broker就成为Controller,负责管理集群的元数据和状态。 - 其他Broker会监听
/controller目录的变化,如果发现/controller节点被删除,就会重新开始竞选。 - 当Controller宕机或失去与ZooKeeper的连接时,会删除
/controller下的临时节点,并释放/controller节点的锁。此时,ZooKeeper会通知其他Broker开始新一轮的竞选。 - 原来的Controller会尝试重新竞选,如果成功,它会恢复到运行状态;如果失败,它会成为Follower,并等待下一次竞选。
Controller要做哪些事
- 监听ZooKeeper上不同的目录。
/admin:这个目录用于存储集群中的管理操作,包括删除Topic、增加分区、重新分配分区等。当有管理操作被触发时,这个目录会更新。监听这个目录来实现Controller处理Topic创建、删除、增加分区等操作;/brokers/ids:这个目录用于存储集群中所有活跃的broker信息,包括broker.id、host、port、rack等。当broker加入或离开集群时,这个目录会更新。监听这个目录来实现Controller监控集群成员与处理Partition的分配与转义等操作;/brokers/topics:这个目录用于存储集群中所有的topic信息,包括topic名称、分区数量、副本分配等。当topic被创建或删除时,这个目录会更新;监听这个目录来实现Controller响应Topic创建删除等操作;/controller:这个目录用于存储当前的controller信息,包括broker.id和controller epoch。当controller发生变化时,这个目录会更新。监听这个目录用于触发Controller的重选举流程;/isr_change_notification:这个目录用于存储集群中的ISR(同步副本集合)变化通知,包括分区名称和新的ISR列表。当ISR发生变化时,这个目录会更新;监听这个目录可以用于提供可靠的消息生产ACK机制。
- 维护元数据信息
- 调用ZooKeeper的API来创建、更新、删除、获取、监听,包括Broker、Topic、Partition、Replica在内的集群元数据信息;
- Controller也会在自己内存中维护这些信息的缓存,用于处理其他Broker获取这些数据的请求
- Controller也会使用Kafka内部特殊的Topic来生产、消费与同步这些元数据信息
- 调用ZooKeeper的API来创建、更新、删除、获取、监听,包括Broker、Topic、Partition、Replica在内的集群元数据信息;
- 维护Partition分配与状态
- Partition分配:由kafka内部的分配策略来决定的,例如DefaultReplicaPlacementStrategy、RackAwareReplicaPlacementStrategy等。用户也可以自定义自己的分配策略,通过实现BrokerRackAwareness接口来定义;
- 维护分区状态机
- 维护每个Partition的状态:NonExistentPartition、NewPartition、OnlinePartition、OfflinePartition。
- Controller来协调和控制各个状态的转移。
- 维护副本状态机。
- 维护每个Partition Replica的状态,包括NewReplica、OnlineReplica、OfflineReplica等。
- Controller来协调和控制各个状态的转移。
(这里有很多内容是关于Kafka如何管理Partition的,包括内部的多个Replica,这里先按下不表,在独立介绍几个模块角色之后,希望能通过具体的操作流程来串联整体)
综合
Broker在一次消息写入时起到了哪些作用?
- 接收消息:Broker接收生产者发送的消息,并将其存储在本地磁盘的分区分段文件中。Broker还会为每个分段文件创建索引文件,以便快速查找消息
- 同步消息:如果Broker是某个分区的Leader副本,它还会将收到的消息同步给其他的Follower副本,以实现数据的备份和高可用性。Broker可以配置同步方式(同步或异步)和同步策略(ISR或Quorum)
- 响应确认:Broker在完成消息的接收和同步后,会根据生产者的acks设置返回相应的确认信息,以通知生产者消息是否写入成功
Broker在消息消费时充当了哪些角色?
- 提供消息:Broker根据Consumer的请求,从本地磁盘的分区分段文件中读取相应的消息,并返回给Consumer]。
- 维护消费位移:Broker会为每个Consumer Group维护一个消费位移(offset),用来记录该Group已经消费到哪个位置。Consumer可以根据自己的消费情况,定期提交消费位移给Broker,也可以查询Broker的消费位移来确定消费的起始位置]。
- 处理Rebalance:Broker会监测Consumer Group内部的变化,比如有新的Consumer加入或者有Consumer退出,然后触发重平衡(rebalance)操作,重新分配每个分区的消费者。Broker会通知Group Leader进行分区分配,并将结果同步给其他的Consumer]。
Broker是如何完成消息的存储的?
这个内容就太多了,放在下一篇吧。