1 集群成员之间的关系
集群中成员之间的信息是通过zk进行维护的
每个broker都有自己的唯一标识
broker启动时通过创建临时节点把自己的ID注册到zk上,然后kafka组件订阅zk中的/brokers/ids路径,当加入或退出集群时,组件会得到通知
如果关闭一个broker,对应的节点会消失,不过这个broker的ID会保留在其他的数据结构中,如果完全关闭一个broker之后用用相同的ID启动一个新的broker,他会立即加入集群,并与旧的broker有着相同的分区和主题
2 控制器
控制器其实就是一个broker,只不过除了具有一般broker的功能之外,还负责分区首领的选举。
集群中第一个启动的broker通过在zk中创建一个临时节点(/controller)让自己成为控制器。其他的节点也会进行创建,只不过会收到一个节点已存在的异常。
其他的broker在控制器节点上创建zk watch对象,可以收到这个节点的变更通知,从而确保集群里一次只有一个控制器存在。
如果控制器被关闭或者与zk断开连接,zk上的临时节点就会小时,其他的节点收到消息后会尝试让自己成为控制器(第一个在zk中成功创建控制器节点的broker成为新的控制器)。
当控制器发现一个broker已经离开集群,他就知道那些失去首领的分区需要一个新的首领,控制器遍历这些分区,并确定谁应该成为新的首领,然后向所有包含新首领或者现有跟随者的broker发送请求,请求包含了谁是新首领以及绅士分区跟随者的信息。
当控制器返现一个新的broker加入集群时,他会使用broker ID来检查新加入的broker时候包含现有分区的副本。如果有,控制器就会把变更通知发给新加入的broker和其他的broker,新broker上的副本开始从首领那里复制消息。
3 复制
1 复制功能时kafka架构的核心
2 kafka是一个分布式的、可分区的、可复制的提交日志服务。
3 复制是可以在个别节点失效时仍能保证kafka的可用性和持久性的重要保证。
4 kafka使用主题来组织数据,每个主题被分为若干个分区,每个分区有多个副本。副本都保存在broker上,每个broker可以保存成百上千个属于不用主题和分区的副本。
5 副本分区的类型:
首领副本:每个分区都有一个首领副本,为了保证一致性,所有生产者请求和消费者请求都会经过这个副本。
跟随着副本:首领以外的所有副本。不处理来自客户端的请求,他们唯一的任务是从首领那里复制消息,保持与首领一致的状态。如果首领崩溃,其中一个会选举为新的首领。
6 首领的作用
所有生产者请求和消费者请求都通过首领副本
搞清楚那个跟随着的状态与自己一致。跟随着为了保持与首领的状态一直,再有新消息到达时,尝试从首领那里复制消息,不过有各种原因会导致同步失败(网络阻塞,broker崩溃等等)。
7 为了与首领保持同步,跟随着向首领发送获取数据的请求,这种请求与消费者为了读取消息而发送的请求是一样的。首领将响应消息发给跟随着。消息中包含跟随着想要获取消息的偏移量,而且这些偏移量是有序的。
8 一个跟随着副本先请求消息1,接着请求消息2,然后请求消息3,在收到这3个请求的相应之前,他是不会发送第4个请求消息的。如果跟随着发送了请求消息4,那么首领就会知道他已经接收到了前面3个请求的响应。通过查看每个跟随着请求的最新偏移量,首领就会知道每个跟随着复制的进度。如果跟随着在10s内没有请求任何消息,或者虽然在请求消息,但是在10s内没有请求新的数据,那么就认为他是不同步的。如果一个副本没有与首领保持一致,在首领失效时,他就不可能成为新的首领。
9 首选首领
除了当前首领之外,每个分区都有一个首选首领--创建主题时选定的首领就是分区的首选首领。
为什么叫首选首领?因为在创建分区时,需要在broker之间均衡首领。
4 处理请求
broker的大部分工作都是在处理客户端、分区副本和控制器发送给分区首领的请求。
Kafka提供了一个二进制协议,该协议制定了请求消息的格式以及broker如何对请求做出响应。
broker按照请求到达的顺序处理请求------>这个使得kafka具有了消息队列的特征,同时保证了保存的消息是有序的。
请求的类型:生产请求:生产者发送的请求
获取请求:在消费者或者跟随着副本需要从broker中获取消息时发送的请求
处理流程:broker会在他监听的每个端口上运行一个Acceptor线程,这个线程会创建一个连接,并把它交给processor线程去处理。它把从客户端获取到请求消息,放进请求队列中,由IO线程进行处理后放进响应队列中,processor从响应队列中获取响应消息。
4.1 元数据请求
情景:
生产请求和获取请求都必须发送给分区的首领副本,当broker收到一个指定分区的请求时,该broker上有没有相对应的首领副本,这时就会抛出异常情况。因此就需要客户端准确的将请求发送到相对应的broker上。
如何实现
客户端使用了元数据请求。
请求中包含了客户端感兴趣的主题列表,发送给任意的broker,broker会响应消息给客户端,响应消息中指明了主题所包含的分区、每个分区都有哪些副本,以及那个是首领副本。
客户端对响应消息进行缓存。
4.2 生产请求
生产者将请求发送给包含分区首领的broker上
1 broker需要对请求进行验证。
* 发送数据的用户是否具有该主题的写入权限
* 请求中的ack是否有效
* 如果ack=all,是否有足够的同步副本保证消息已经被安全写入
2 将消息写入磁盘
首先将消息写入缓冲区,然后刷写到磁盘中
当然kafka不可能等待数据刷写到磁盘,会通过复制功能保证他的持久性
当消息写入到分区首领之后,broker检查ack,如果是0或者1返回响应,如果是all,请求就会被存放到一个叫炼狱的缓冲区中,直到所有的副本都完成写入才会返回响应
4.3获取请求
1 获取请求的处理方式和生产请求的处理方式差不多
2 总的来说就是:客户端发送请求,向broker请求主题分区中具有特定偏移量的消息,同时可以指定从broker可以从分区中返回的数据的大小的上下限。
上限是因为客户端需要为返回的数据分配足够的内存。
下限是为了减少cpu和网络开销。
broker会等到数据达到一定的量才会给客户端返回数据,这个时候,客户端是不断发出请求的,当然这里也会配置超时时间,如果到达一定的时间间隔后,也会将数据进行返回。
3 具体处理:客户端向broker发送请求,首领分区会先检查请求是否有效(其实就是检查请求中得偏移量是否有效),如果偏移量有效得话,broker将按照客户端指定得数量上限从分区中读取,再把消息返回给客户端。
当数据从broker返回给客户端得过程中使用了0拷贝得技术,所谓得0拷贝就是首领分区传给broker得数据或者文件不经过任何得缓冲区直接通过网络通道返回给客户端。提高效率。
5 物理存储
1 kafka得基本存储单元:分区
2 分区无法在多个broker之间细分,也无法在同一个broker的不同磁盘之间再次进行细分。这就导致了分区的大小收到了单个挂载点可用空间的限制。
5.1 分配分区
在创建分区时,kafka首先会决定如何在broker之间分配分区
假如有6个broker,10个分区,每个分区3个副本
达成的目标:
1. 在broker之间平均分配分区副本
2. 确保分区中的每个副本都在不同的broker上
3. 如果为broker指定机架信息的话,尽可能把每个分区的副本放到不同机架的broker上
分区的分配方式:
同一机架:以轮询的方式分配首领分区副本,之后跟随着依次在broker上分配。
不同机架:机架之间交替分配。
将分区分给对应的broker之后,开始分配目录
将新分区添加到分区数量最少的目录中
5.2 文件管理
kafka是可以保留数据的,但不会一直保留
kafka管理员为每个主题配置了数据保留权限
一般我们会把分区分成若干个片段--->原因:在大文件中查找和删除数据容易出错
--->默认情况下,每个分段有1G或者一周的数据
--->当前正在写入数的片段是活跃片段,这个永远不会被删除