//调用adminClient创建Topic
val createResult = adminClient.createTopics(Collections.singleton(newTopic))
createResult.all().get()
println(s"Created topic ${topic.name}.")
} else {
throw new IllegalArgumentException(s"Topic ${topic.name} already exists")
}
}
-
检查各项入参是否有问题
-
adminClient.listTopics()
,然后比较是否已经存在待创建的Topic;如果存在抛出异常; -
判断是否配置了参数
--replica-assignment
; 如果配置了,那么Topic就会按照指定的方式来配置副本情况 -
解析配置
--config
配置放到configsMap
中;configsMap
给到NewTopic
对象 -
调用
adminClient.createTopics
创建Topic; 它是如何创建Topic的呢?往下分析源码
3.1 KafkaAdminClient.createTopics(NewTopic) 创建Topic
@Override
public CreateTopicsResult createTopics(final Collection newTopics,
final CreateTopicsOptions options) {
//省略部分源码…
Call call = new Call(“createTopics”, calcDeadlineMs(now, options.timeoutMs()),
new ControllerNodeProvider()) {
@Override
public CreateTopicsRequest.Builder createRequest(int timeoutMs) {
return new CreateTopicsRequest.Builder(
new CreateTopicsRequestData().
setTopics(topics).
setTimeoutMs(timeoutMs).
setValidateOnly(options.shouldValidateOnly()));
}
@Override
public void handleResponse(AbstractResponse abstractResponse) {
//省略
}
@Override
void handleFailure(Throwable throwable) {
completeAllExceptionally(topicFutures.values(), throwable);
}
};
}
这个代码里面主要看下Call里面的接口; 先不管Kafka如何跟服务端进行通信的细节; 我们主要关注创建Topic的逻辑;
createRequest
会构造一个请求参数CreateTopicsRequest
例如下图
- 选择ControllerNodeProvider这个节点发起网络请求
可以清楚的看到, 创建Topic这个操作是需要Controller来执行的;
4. 发起网络请求
5. Controller角色的服务端接受请求处理逻辑
首先找到服务端处理客户端请求的 源码入口 ⇒ KafkaRequestHandler.run()
主要看里面的 apis.handle(request)
方法; 可以看到客户端的请求都在request.bodyAndSize()
里面
5.1 KafkaApis.handle(request) 根据请求传递Api调用不同接口
进入方法可以看到根据request.header.apiKey
调用对应的方法,客户端传过来的是CreateTopics
5.2 KafkaApis.handleCreateTopicsRequest 处理创建Topic的请求
def handleCreateTopicsRequest(request: RequestChannel.Request): Unit = {
// 部分代码省略
//如果当前Broker不是属于Controller的话,就抛出异常
if (!controller.isActive) {
createTopicsRequest.data.topics.asScala.foreach { topic =>
results.add(new CreatableTopicResult().setName(topic.name).
setErrorCode(Errors.NOT_CONTROLLER.code))
}
sendResponseCallback(results)
} else {
// 部分代码省略
}
adminManager.createTopics(createTopicsRequest.data.timeoutMs,
createTopicsRequest.data.validateOnly,
toCreate,
authorizedForDescribeConfigs,
handleCreateTopicsResults)
}
}
-
判断当前处理的broker是不是Controller,如果不是Controller的话直接抛出异常,从这里可以看出,CreateTopic这个操作必须是Controller来进行, 出现这种情况有可能是客户端发起请求的时候Controller已经变更;
-
调用
adminManager.createTopics()
5.3 adminManager.createTopics()
创建主题并等等主题完全创建,回调函数将会在超时、错误、或者主题创建完成时触发
该方法过长,省略部分代码
def createTopics(timeout: Int,
validateOnly: Boolean,
toCreate: Map[String, CreatableTopic],
includeConfigsAndMetatadata: Map[String, CreatableTopicResult],
responseCallback: Map[String, ApiError] => Unit): Unit = {
// 1. map over topics creating assignment and calling zookeeper
val brokers = metadataCache.getAliveBrokers.map { b => kafka.admin.BrokerMetadata(b.id, b.rack) }
val metadata = toCreate.values.map(topic =>
try {
//省略部分代码
//检查Topic是否存在
//检查 --replica-assignment参数和 (–partitions || --replication-factor ) 不能同时使用
// 如果(–partitions || --replication-factor ) 没有设置,则使用 Broker的配置(这个Broker肯定是Controller)
// 计算分区副本分配方式
createTopicPolicy match {
case Some(policy) =>
//省略部分代码
adminZkClient.validateTopicCreate(topic.name(), assignments, configs)
if (!validateOnly)
adminZkClient.createTopicWithAssignment(topic.name, configs, assignments)
case None =>
if (validateOnly)
//校验创建topic的参数准确性
adminZkClient.validateTopicCreate(topic.name, assignments, configs)
else
//把topic相关数据写入到zk中
adminZkClient.createTopicWithAssignment(topic.name, configs, assignments)
}
}
- 做一些校验检查
①.检查Topic是否存在
②. 检查--replica-assignment
参数和 (--partitions || --replication-factor
) 不能同时使用
③.如果(--partitions || --replication-factor
) 没有设置,则使用 Broker的配置(这个Broker肯定是Controller)
④.计算分区副本分配方式
-
createTo