spring-kafka源码阅读(1)

SPRING-KAFKA源码

最近看了一点spring-kafka的东西, 看到网上相关的东西比较少, 就想着自己整理一把.

相关介绍

kakfa

kafka

spring-kafka

spring-kafka

spring-kafka demo

producer

@Component
public class Producer {
    
    ...
    
    @Autowired
    private KafkaTemplate template;

    public void sendFoo(String what) {
        this.template.send("topic1", new Foo1(what));
    }
    
}

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 时序图

  1. KafkaListenerAnnotationBeanPostProcessor#postProcessBeforeInitialization : 不做任何处理, 直接返回bean;

  2. 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执行结束;

  1. 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的逻辑;

  1. 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;

  1. 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 4个接口 :

  • DisposableBean :提供一个销毁bean时候触发的回调方法;

  • SmartLifecycle :介绍 拓展了Lifecycle接口,提供了bean生命周期各个阶段的回调方法;继承了Phased,用于提供一个先后顺序:启动时,越大越晚调用;结束时,则相反。

  • ApplicationContextAware :自动装配ApplicationContext;

  • ApplicationListener :监听应用上下文事件;

  1. KafkaListenerEndpointRegistry#start
public void start() {
        for (MessageListenerContainer listenerContainer : getListenerContainers()) {
            // 核心逻辑见2
            startIfNecessary(listenerContainer);
        }
}

通过start这个入口开始整个listener启动的过程;

  1. 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);
            }
        }
    }

  1. 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);
}
  1. 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);
            
        }

你可能感兴趣的:(spring-kafka源码阅读(1))