ZooKeeper的协调服务是利用外部的一个分布式协调服务 ZooKeeper实现的

那么我们说下ZK如何跟Kafka结合起来的

ZK是一个特殊的中间件,好比一个瑞士军刀,提供了很多基本的操作,支持自定义组合的

ZK作为一个分布式的系统,可以解决分布式集群中,应用系统面对的各种通用的一致性问题

ZK本身支持集群部署,可以通过选举出一个Leader,选举的原则是半数以上,所以推荐奇数节点部署

ZK内部是一种UNIX系统类似的树状存储结构

树状结构中存在多个节点,每一个都叫做ZNode,其中有一种临时节点,这个节点的特点在于,如果创建临时节点的客户端失去了连接,这个临时节点就会消失,在ZK内部,利用了心跳请求来判断是否需要删除客户端创建的临时节点

ZK还提供了一种订阅节点的Watcher机制,一旦节点的状态发生了变化,订阅的客户端会收到通知

利用这两点,可以获取到各个节点的存活状态,那么Kafka具体的实现,基本如下

图片

圆角的是临时节点,直角的持久节点

左边保存的是Broker信息,如果一个Broker上线了,那么会在ZK中创建一个临时节点,节点内保存了地址 版本号,启动时间等信息

右侧的则是保存了主题和分区的信息,topics下面每个节点都是一个主题

节点的名称就是主题的名称,例如topicA,然后每个主题下面有一个固定的partitions节点

下面包含了所有分区

每个分区下面有一个名为state的临时节点,保存着这个分区的leader和ISR的BrokerID,这个state临时节点是由这个Leader Broker创建的,如果这个Leader宕机了,那么这个节点会消失,直到新的Leader的出现

客户端如何获取到连接的Broker的呢?

通过上面的ZK的数据结构,我们必然是通过主题找到分区,然后是下面的state节点,获取到对应的BrokerID,然后去找到真正的访问地址

上面的过程,对应的源码,在

NetworkClient的poll方法中实现,调用最后到maybeUpdate(long,Node)中

在这个方法中,我们构建了一个更新元数据请求的构造器,然后放入一个等待发送的队列中

private long maybeUpdate(long now, Node node) {

String nodeConnectionId = node.idString();

if (canSendRequest(nodeConnectionId, now)) {

// 构建一个更新元数据的请求的构造器

Metadata.MetadataRequestAndVersion metadataRequestAndVersion = metadata.newMetadataRequestAndVersion();

inProgressRequestVersion = metadataRequestAndVersion.requestVersion;

MetadataRequest.Builder metadataRequest = metadataRequestAndVersion.requestBuilder;

log.debug(“Sending metadata request {} to node {}”, metadataRequest, node);

// 发送更新元数据的请求

sendInternalMetadataRequest(metadataRequest, nodeConnectionId, now);

return defaultRequestTimeoutMs;

}

//…

}

构造器构造是利用了一个内部构造器Builder,直接放到了队列中

然后直到真正的发出去的时候,才会调用build方法,真正的建造出去

在sendInternalMetadataRequest的时候,只是放在了发送队列,没有真正的发送

具体的内容封装在

MetadataRequest中

内部只有一个主题的列表,表明需要获取哪些主题的元数据

在Broker端,如何处理这个更新请求的

Broker的处理方式,在KafkaApis的handler中的

handleTopicMetadataRequest(RequestChannel.Request)

基本的流程就是先去获取元数据,然后去获取真正的Borker,构建真正的响应给客户端

Kafka在每个Borker中都维护了一份和ZK一样的元数据缓存,而不是每次请求都去去读ZK,毕竟注册了观察者,如果元数据发生了变化,可以直接感知到

我们本章说了ZK和Kafka的关系

ZK的能力很强大,基本是开源界的瑞士军刀,提供了诸多的能力,方便构建一个分布式的系统

但是,ZK并不是完美的

我们并不能往ZK中写入过多的数据,因为ZK写入超过几百MB之后,性能和稳定都会验证下降

而且,不要让可用性过度的依赖ZK,避免ZK宕机导致系统本身不能使用

Kafka和ZK的集合,主要就是保存了Broker的列表和主题分区信息两颗树,让客户端可以获取到对应的节点数据,但是这样,导致了过度依赖ZK,导致Kafka也不能工作

对于部署大规模的Kafka集群,建议是将其拆分为互相独立的小集群部署,每个小集群使用一组独立的ZK提供服务,这样,每个ZK中存储的数据相对比较少,某个ZK集群出现了故障,只会影响了一个小的Kafka集群,故障的影响面小

发表评论

邮箱地址不会被公开。 必填项已用*标注