SPRING-KAFKA源码
最近看了一点spring-kafka的东西, 看到网上相关的东西比较少, 就想着自己整理一把.
相关介绍
kakfa
kafka
spring-kafka
spring-kafka
spring-kafka demo
producer
@Component
public class Producer {
...
@Autowired
private KafkaTemplate
consumer
@Component
public class Consumer {
...
@KafkaListener(id = "fooGroup", topics = "topic1")
public void listen(Foo2 foo) {
logger.info("Received: " + foo);
if (foo.getFoo().startsWith("fail")) {
throw new RuntimeException("failed");
}
}
@KafkaListener(id = "dltGroup", topics = "topic1.DLT")
public void dltListen(String in) {
logger.info("Received from DLT: " + in);
}
}
@EnableKafka
从@EnableKafka看起,
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(KafkaBootstrapConfiguration.class)
public @interface EnableKafka {
}
SPRING-KAFKA通过该注解启动,触发包括一些相关的默认bean的初始化和配置,listener的
发现和注册等,它仅是一个标记注解,但是进一步引入了KafkaBootstrapConfiguration这
个类.
@Configuration
public class KafkaBootstrapConfiguration {
@SuppressWarnings("rawtypes")
@Bean(name = KafkaListenerConfigUtils.KAFKA_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public KafkaListenerAnnotationBeanPostProcessor kafkaListenerAnnotationProcessor() {
return new KafkaListenerAnnotationBeanPostProcessor();
}
@Bean(name = KafkaListenerConfigUtils.KAFKA_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
public KafkaListenerEndpointRegistry defaultKafkaListenerEndpointRegistry() {
return new KafkaListenerEndpointRegistry();
}
}
该配置类中声明了KafkaListenerEndpointRegistry的一个默认bean和KafkaListenerAnnotationBeanPostProcessor的bean.
KafkaListenerEndpointRegistry
这个类基于KafkaListenerEndpoint提供的信息,创建相关的MessageListenerContainer实例,注册到其中,同时也交由registry管理containers的生命周期,而不通过Spring去管理。
MessageListenerContainer
该接口有2个基本的实现:KafkaMessageListenerContainer和ConcurrentMessageListenerContainer,其中ConcurrentMessageListenerContainer是组合了多个KafkaMessageListenerContainer而实现。
ConcurrentMessageListenerContainer
在后面启动阶段中,根据配置concurrency的数值生成多个containers管理起来,每个container皆为KafkaMessageListenerContainer的实例,也需要启动;
KafkaMessageListenerContainer
每个container包含一个ListenerConsumer,它负责消息线程的轮询,消费。
KafkaListenerEndpoint
listener endpoint的基本模型的接口,包括id,分组,主题,分区等的信息。
KafkaListenerEndpointRegistrar
帮助注册endpoint到registry的工具类。
KafkaListenerAnnotationBeanPostProcessor
public class KafkaListenerAnnotationBeanPostProcessor
implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {
...
}
这个类是Spring-Kafka启动的核心逻辑所在。它实现了BeanPostProcessor,Ordered,BeanFactoryAware,SmartInitializingSingleton:
BeanPostProcessor : 介绍
对于每一个bean在初始化的过程中都会被BeanPostProcessor管理到,其中 :
i. postProcessorBeforeInitailization方法是在bean实例化,依赖注入之后及自定义初始化方法(例如:配置文件中bean标签添加init-method属性指定Java类中初始化方 法、@PostConstruct注解指定初始化方法,Java类实现InitailztingBean接口)之前调用;
ii. postProcessorAfterInitailization方法是在bean实例化、依赖注入及自定义初始化方法之后调用;Ordered : 定义优先级,其值为
LOWEST_PRECEDENCE
(最低);BeanFactoryAware :引入BeanFactory;
SmartInitializingSingleton : 介绍(最后的部分)
在所有单例bean都初始化完毕之后才会发起调用afterSingletonsInstantiated(...);
注册过程
// TODO 时序图
KafkaListenerAnnotationBeanPostProcessor#postProcessBeforeInitialization : 不做任何处理, 直接返回bean;
KafkaListenerAnnotationBeanPostProcessor#postProcessAfterInitialization :
@Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
// 判断类中是否包含@KafkaListener(s)
...
if (annotatedMethods.isEmpty()) {
...
} else {
// 存在有@KafkaListener(s)
for (Map.Entry> entry : annotatedMethods.entrySet()) {
Method method = entry.getKey();
for (KafkaListener listener : entry.getValue()) {
processKafkaListener(listener, method, bean, beanName);
}
}
...
}
...
}
return bean;
}
对于每一个bean,判断它是否含有类级别和方法级别的@KafkaListener(s)注解,有,则完成相关的监听器的注册。这里先以方法级别的@KafkaListener(s)为主,分析启动过程。
2.1 processKafkaListener(kafkaListener, method, bean, beanName) :创建endpoint对象;
2.2 processListener(endpoint, kafkaListener, bean, methodToUse, beanName) :
protected void processListener(MethodKafkaListenerEndpoint, ?> endpoint, KafkaListener kafkaListener, Object bean, Object adminTarget, String beanName) {
...
// 完善endpoint的属性
endpoint.setBean(bean);
...
// 确定factory
KafkaListenerContainerFactory> factory = null;
...
endpoint.setBeanFactory(this.beanFactory);
...
// 注册endpoint
this.registrar.registerEndpoint(endpoint, factory);
...
}
完善endpoint的对象,获取类型为KafkaListenerContainerFactory的factroy对象,然后调用registrar的registerEndpoint(endpoint, factory);
2.3 KafkaListenerEndpointRegistrar#registerEndpoint(endpoint, factory) :
public void registerEndpoint(KafkaListenerEndpoint endpoint, KafkaListenerContainerFactory> factory) {
// 整理成一个descriptor
KafkaListenerEndpointDescriptor descriptor = ...
synchronized (this.endpointDescriptors) {
if (this.startImmediately) {
// Register and start immediately
// 基本没走这个分支
...
} else {
this.endpointDescriptors.add(descriptor);
}
}
}
将endpoint和factory整理到KafkaListenerEndpointDescriptor中,存储到该类的一个List
至此postProcessAfterInitialization执行结束;
- KafkaListenerAnnotationBeanPostProcessor#afterSingletonsInstantiated :
public void afterSingletonsInstantiated() {
// 给registrar设置beanFactory,这里一般是ConcurrentKafkaListenerContainerFactory
this.registrar.setBeanFactory(this.beanFactory);
...
// 设置endpointRegistry这个在KafkaBootstrapConfiguration中实例化了一个bean出来
if (this.registrar.getEndpointRegistry() == null) {
if (this.endpointRegistry == null) {
...
this.endpointRegistry = ...
}
this.registrar.setEndpointRegistry(this.endpointRegistry);
}
...
// Set the custom handler method factory once resolved by the configurer
MessageHandlerMethodFactory handlerMethodFactory = ...
...
// Actually register all listeners
// 真正注册所有listener的地方
this.registrar.afterPropertiesSet();
}
所有单例bean实例化完成后执行,为registrar设置BeanFactory、endpointRegistry等,然后调用真正去注册每个listener的逻辑;
- KafkaListenerEndpointRegistrar#registerAllEndpoints :
protected void registerAllEndpoints() {
synchronized (this.endpointDescriptors) {
for (KafkaListenerEndpointDescriptor descriptor : this.endpointDescriptors) {
this.endpointRegistry.registerListenerContainer(
descriptor.endpoint, resolveContainerFactory(descriptor));
}
// trigger immediate startup
// 触发启动标记设置为true
this.startImmediately = true;
}
}
遍历前面整理出来的endpointDescriptors的信息,注册到endpointRegistry;
- KafkaListenerEndpointRegistry#registerListenerContainer :
public void registerListenerContainer(KafkaListenerEndpoint endpoint, KafkaListenerContainerFactory> factory, boolean startImmediately) {
String id = endpoint.getId();
synchronized (this.listenerContainers) {
// 创建container, 加入listenerContainers中
MessageListenerContainer container = createListenerContainer(endpoint, factory);
this.listenerContainers.put(id, container);
if (...) {
List containerGroup;
// 获取(或创建)containerGroup, 然后将container加入其中
...
containerGroup.add(container);
}
// 这里传入的startImmediately == false, 故下面逻辑此处不执行
if (startImmediately) {
startIfNecessary(container);
}
}
}
5.1 createListenerContainer(endpoint, factory) :
protected MessageListenerContainer createListenerContainer(KafkaListenerEndpoint endpoint,
KafkaListenerContainerFactory> factory) {
// 通过工厂,创建listenerContainer对象
// factory的具体类是ConcurrentKafkaListenerContainerFactory
// createListenerContainer方法在抽象类AbstractKafkaListenerContainerFactory中
// 见5.2
MessageListenerContainer listenerContainer = factory.createListenerContainer(endpoint);
// 判断是否需要执行InitializingBean的初始化方法
if (listenerContainer instanceof InitializingBean) {
...
}
// 一段关于containerPhase的逻辑
...
return listenerContainer;
}
5.2 AbstractKafkaListenerContainerFactory#createListenerContainer
public C createListenerContainer(KafkaListenerEndpoint endpoint) {
// 创建一个空的container实例
// 具体实现在ConcurrentKafkaListenerContainerFactory类中
// 里头主要涉及到“有无patitions配置”而不同的参数设置
C instance = createConainerInstance(endpoint);
...
// 设置endpoint的一些值
...
// 见5.3
endpoint.setupListenerContainer(instance, this.messageConverter);
//
initializeContainer(instance, endpoint);
return instance;
}
5.3 AbstractKafkaListenerEndpoint#setupMessageListener :
private void setupMessageListener(MessageListenerContainer container, MessageConverter messageConverter) {
// createMessageListener的具体逻辑落到了MethodKafkaListenerEndpoint#
// createMessageListener中
// 见5.4
MessagingMessageListenerAdapter adapter = createMessageListener(container, messageConverter);
...
Object messageListener = adapter;
// retry的设置
// 装饰器模式
if (this.retryTemplate != null) {
...
messageListener = new RetryingMessageListenerAdapter<>(...);
...
}
// recordFilterStrategy(记录过滤策略)的设置
if (this.recordFilterStrategy != null) {
....
// 根据是否批量,传入不同的入参
// 装饰器模式
messageListener = new FilteringBatchMessageListenerAdapter<>(...);
....
}
container.setupMessageListener(messageListener);
}
5.4 MethodKafkaListenerEndpoint#createMessageListener :
protected MessagingMessageListenerAdapter createMessageListener(MessageListenerContainer container,
MessageConverter messageConverter) {
// 见5.5
MessagingMessageListenerAdapter messageListener = createMessageListenerInstance(messageConverter);
// 封装一下消费者消费逻辑的方法信息,用于消费时回调用
messageListener.setHandlerMethod(configureListenerAdapter(messageListener));
// replyTopic, replyTemplate
...
return messageListener;
}
5.5 MethodKafkaListenerEndpoint#createMessageListenerInstance :
protected MessagingMessageListenerAdapter createMessageListenerInstance(MessageConverter messageConverter) {
MessagingMessageListenerAdapter listener;
// 是否为批量监听器
if (isBatchListener()) {
BatchMessagingMessageListenerAdapter messageListener = ...
...
listener = messageListener;
} else {
RecordMessagingMessageListenerAdapter messageListener = ...
...
listener = messageListener;
}
...
return listener;
}
启动过程
// TODO 时序图2
KafkaListenerEndpointRegistry实现了DisposableBean, SmartLifecycle, ApplicationContextAware, ApplicationListener
DisposableBean :提供一个销毁bean时候触发的回调方法;
SmartLifecycle :介绍 拓展了Lifecycle接口,提供了bean生命周期各个阶段的回调方法;继承了Phased,用于提供一个先后顺序:启动时,越大越晚调用;结束时,则相反。
ApplicationContextAware :自动装配ApplicationContext;
ApplicationListener :监听应用上下文事件;
- KafkaListenerEndpointRegistry#start
public void start() {
for (MessageListenerContainer listenerContainer : getListenerContainers()) {
// 核心逻辑见2
startIfNecessary(listenerContainer);
}
}
通过start这个入口开始整个listener启动的过程;
- ConcurrentMessageListenerContainer#doStart :
protected void doStart() {
if (!isRunning()) {
...
TopicPartitionInitialOffset[] topicPartitions = ...
// concurrency值至少要和topicPartitions的值一致
if (topicPartitions != null && this.concurrency > topicPartitions.length) {
this.concurrency = topicPartitions.length;
}
setRunning(true);
for (int i = 0; i < this.concurrency; i++) {
KafkaMessageListenerContainer container;
if (topicPartitions == null) {
container = new KafkaMessageListenerContainer<>(...);
} else {
// 多partitions的情况
container = new KafkaMessageListenerContainer<>(...);
}
// container一些属性的设置
...
// 启动,见3
container.start();
this.containers.add(container);
}
}
}
- KafkaMessageListenerContainer#doStart :
protected void doStart() {
...
if (!this.consumerFactory.isAutoCommit()) {
// ack的一些配置
AckMode ackMode = ...
}
Object messageListener = containerProperties.getMessageListener();
if (containerProperties.getConsumerTaskExecutor() == null) {
// 配置消费任务的任务执行器
SimpleAsyncTaskExecutor consumerExecutor = ...
containerProperties.setConsumerTaskExecutor(consumerExecutor);
}
this.listener = (GenericMessageListener>) messageListener;
ListenerType listenerType = ListenerUtils.determineListenerType(this.listener);
// DelegatingMessageListener相关的一些设置
...
// 创建consumer,见4
this.listenerConsumer = new ListenerConsumer(this.listener, listenerType);
setRunning(true);
// 异步提交consumer
this.listenerConsumerFuture = containerProperties
.getConsumerTaskExecutor()
.submitListenable(this.listenerConsumer);
}
- ListenerConsumer :实现了SchedulingAwareRunnable, ConsumerSeekCallback这2个接口:
- SchedulingAwareRunnable :拓展与Runnable接口,提供一种长时间运行的操作的回调;
- ConsumerSeekCallback :用于定位一条消息,从主题,分区,偏移量3个维度;
构造器 :
ListenerConsumer(GenericMessageListener> listener, ListenerType listenerType) {
// 创建kafka的consumer,详情见DefaultKafkaConsumerFactory#createConsumer
final Consumer consumer =
KafkaMessageListenerContainer.this.consumerFactory.createConsumer(
this.consumerGroupId,
this.containerProperties.getClientId(),
KafkaMessageListenerContainer.this.clientIdSuffix);
this.consumer = consumer;
// TODO 一个新的专题
ConsumerRebalanceListener rebalanceListener = createRebalanceListener(consumer);
if (KafkaMessageListenerContainer.this.topicPartitions == null) {
// 订阅主题,包括topic包含与不包含通配符的2种情况
consumer.subscribe(...);
} else {
// 配置带有主题,分区,偏移量等情况时的设置
List topicPartitions = ...
this.definedPartitions = ...
for (TopicPartitionInitialOffset topicPartition : topicPartitions) {
this.definedPartitions.put(topicPartition.topicPartition(),
new OffsetMetadata(topicPartition.initialOffset(), topicPartition.isRelativeToCurrent(),
topicPartition.getPosition()));
}
consumer.assign(new ArrayList<>(this.definedPartitions.keySet()));
}
// 属性设置,此处代码精简掉很多
this.genericListener = ...
this.listener = ...
this.batchListener = ...
this.isBatchListener = ...
this.wantsFullRecords = ...
this.listenerType = ...
this.isConsumerAwareListener = ...
// errorHandler设置
...
// transactionTemplate的设置
...
// scheduler设置或创建
if (this.containerProperties.getScheduler() != null) {
this.taskScheduler = this.containerProperties.getScheduler();
this.taskSchedulerExplicitlySet = true;
} else {
ThreadPoolTaskScheduler threadPoolTaskScheduler = ...
threadPoolTaskScheduler.initialize();
this.taskScheduler = threadPoolTaskScheduler;
}
// 消费者启动监控定时任务
this.monitorTask = this.taskScheduler.scheduleAtFixedRate(() -> checkConsumer(),
this.containerProperties.getMonitorInterval() * 1000);
}