上一章我们了解到KafkaAutoConfiguration自动初始化依赖Bean,如下图所示若应用中不包含kafkaAdmin对象,KafkaAutoConfiguration为我们应用创建一个,那么kafkaAdmin这个类的作用是什么呢?
@Bean
@ConditionalOnMissingBean
public KafkaAdmin kafkaAdmin() {
//初始化kafkaAdmin对象用于管理topic等元数据
KafkaAdmin kafkaAdmin = new KafkaAdmin(this.properties.buildAdminProperties());
//如果为ture 初始化期间无法连接到代理时无法加载应用程序上下文的情况下检查/添加主题
kafkaAdmin.setFatalIfBrokerNotAvailable(this.properties.getAdmin().isFailFast());
return kafkaAdmin;
}
在kafka应用由consumer角色负责消费数据,producer角色负责生产消息。那么KafkaAdmin(父类AdminClient)的作用就是对于topic的一些列管理包括topic的新增(partition,replicas分配),删除,修改(查看源码可以知道真正的执行者是KafkaAdminClient类)
springboot2.x中如何去创建topic呢,提供了多种方式。
/**
*
*@author fangyuan
* 构建kafka topic如果topic已经存在,忽略Bean
*/
@Configuration
public class TopicConfigure {
@Bean
public KafkaAdmin kafkaAdmin() {
//这里没有使用spring boot 提供的yml的方式注入到properties中
Map<String, Object> props = new HashMap<>(1);
//配置Kafka实例的连接地址
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//设置配置文件
KafkaAdmin admin = new KafkaAdmin(props);
admin.setFatalIfBrokerNotAvailable(false);
return admin;
}
@Bean
public NewTopic createSpringboot_test_topic(){
//可以直接指定分区数和副本数,副本数的分布由kafka集群自动分配
return TopicBuilder.name("springboot_test_topic")
.partitions(3)
.replicas(1)
//设置是否日志清除策略 compact 压缩 delete
.compact()
.build();
}
/**
*
* @return
*/
@Bean
public NewTopic createPersonTopic(){
//手动分配不同partition的副本分布情况,如下所示我们指定该topic
//存在3个分区,并指定各分区副本数指定落到指定的broker中,其中0,1,2为brokerid
return TopicBuilder.name("personTopic")
.assignReplicas(0, Arrays.asList(0,1,2))
.assignReplicas(1, Arrays.asList(1,2,0))
.assignReplicas(2, Arrays.asList(2,0,1))
//设置是否日志清除策略 compact 压缩 delete
.compact()
.build();
}
}
那么springboot2.x如何创建呢,我们来追踪源码分析:
当我们通过上述初始化KafkaAdmin,NewTopic后。因为KafkaAdmin实现SmartInitializingSingleton接口如下图所示:
//KafkaAdmin 默认实现ApplicationContextAware 容器启动会自动注入ApplicationContext spirng核心上下文类 已经指定KafkaAdmin 为单列
public class KafkaAdmin implements ApplicationContextAware, SmartInitializingSingleton {
.....
}
spring容器执行SmartInitializingSingleton中的回调方法afterSingletonsInstantiated(SmartInitializingSingleton实现该接口后,当所有单例 bean 都初始化完成以后, 容器会回调该接口的方法 afterSingletonsInstantiated),
@Override
public void afterSingletonsInstantiated() {
this.initializingContext = true;
//autoCreate默认为true 随容器自启动
if (this.autoCreate) {
initialize();
}
}
//初始化
public final boolean initialize() {
//获取系统中已被初始化NewTopic集合
Collection<NewTopic> newTopics = this.applicationContext.getBeansOfType(NewTopic.class, false, false).values();
//如果系统有指定新topics
if (newTopics.size() > 0) {
AdminClient adminClient = null;
try {
//获取真正执行器KafkaAdminClient对象
adminClient = AdminClient.create(this.config);
}
catch (Exception e) {
if (!this.initializingContext || this.fatalIfBrokerNotAvailable) {
throw new IllegalStateException("Could not create admin", e);
}
else {
LOGGER.error(e, "Could not create admin");
}
}
if (adminClient != null) {
try {
//如果kafka不存在中对应topic添加topic
addTopicsIfNeeded(adminClient, newTopics);
return true;
}
catch (Exception e) {
if (!this.initializingContext || this.fatalIfBrokerNotAvailable) {
throw new IllegalStateException("Could not configure topics", e);
}
else {
LOGGER.error(e, "Could not configure topics");
}
}
finally {
this.initializingContext = false;
//关闭adminClient
adminClient.close(this.closeTimeout);
}
}
}
this.initializingContext = false;
return false;
}
private void addTopicsIfNeeded(AdminClient adminClient, Collection<NewTopic> topics) {
if (topics.size() > 0) {
Map<String, NewTopic> topicNameToTopic = new HashMap<>();
topics.forEach(t -> topicNameToTopic.compute(t.name(), (k, v) -> t));
DescribeTopicsResult topicInfo = adminClient
.describeTopics(topics.stream()
.map(NewTopic::name)
.collect(Collectors.toList()));
List<NewTopic> topicsToAdd = new ArrayList<>();
//验证topic的partitions数 目的是判断存在的NewTopic指定partition数和实际partition数是否一致,不一致会修改kafka中partitions也就执行modifyTopics()方法
Map<String, NewPartitions> topicsToModify = checkPartitions(topicNameToTopic, topicInfo, topicsToAdd);
if (topicsToAdd.size() > 0) {
//添加topic 具体实现细节可以看KafkaAdminClient提供的各种对kafka操作
addTopics(adminClient, topicsToAdd);
}
if (topicsToModify.size() > 0) {
//修改topic
modifyTopics(adminClient, topicsToModify);
}
}
}
以上就是springboot2.x对kafka topic的初始化过程
如果系统需要手动对kafka topic管理 可以通过引用KafkaAdmin(单列) 对象来进行操作
Demo项目github地址:https://github.com/fangyuan94/kafkaDemo
下一章:springboot2.x +kafka使用和源码分析三(生产者配置)
8万+



被折叠的 条评论
为什么被折叠?



