Rocketmq源码分析(1)

复制代码
此次源码分析-rocketmq-spring-boot-starter,starter众所周知入口点就是AutoConfiguration.


RocketMQAutoConfiguration.class
java 复制代码
// 标识为配置类
@Configuration
//将RocketMQProperties识别为配置属性类,创建对象并注入到spring容器中
@EnableConfigurationProperties(RocketMQProperties.class)
// 当类路径中有MQAdmin.class 才启用本配置
@ConditionalOnClass({MQAdmin.class})
//条件启用配置,如果配置rocketmq.name-server不存在,默认会加载此配置,如果没有配置havingValue就意味着rocketmq.name-server 不是 false 就会加载此配置
@ConditionalOnProperty(prefix = "rocketmq", value = "name-server", matchIfMissing = true)
// 把MessageConverterConfiguration、ListenerContainerConfiguration、ExtProducerResetConfiguration、RocketMQTransactionConfiguration 当做配置类加载进来
@Import({MessageConverterConfiguration.class, ListenerContainerConfiguration.class, ExtProducerResetConfiguration.class, RocketMQTransactionConfiguration.class})
// 指定该配置类会在MessageConverterConfiguration配置类的后面加载
@AutoConfigureAfter({MessageConverterConfiguration.class})
// 指定该配置类会在RocketMQTransactionConfiguration配置类的前面加载
@AutoConfigureBefore({RocketMQTransactionConfiguration.class})

public class RocketMQAutoConfiguration {
    private static final Logger log = LoggerFactory.getLogger(RocketMQAutoConfiguration.class);

    public static final String ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME =
        "rocketMQTemplate";

    @Autowired
    private Environment environment;

    @PostConstruct
    public void checkProperties() {
        String nameServer = environment.getProperty("rocketmq.name-server", String.class);
        log.debug("rocketmq.nameServer = {}", nameServer);
        if (nameServer == null) {
            log.warn("The necessary spring property 'rocketmq.name-server' is not defined, all rockertmq beans creation are skipped!");
        }
    }

    @Bean
    @ConditionalOnMissingBean(DefaultMQProducer.class)
    @ConditionalOnProperty(prefix = "rocketmq", value = {"name-server", "producer.group"})
    public DefaultMQProducer defaultMQProducer(RocketMQProperties rocketMQProperties) {
        RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer();
        String nameServer = rocketMQProperties.getNameServer();
        String groupName = producerConfig.getGroup();
        Assert.hasText(nameServer, "[rocketmq.name-server] must not be null");
        Assert.hasText(groupName, "[rocketmq.producer.group] must not be null");

        String accessChannel = rocketMQProperties.getAccessChannel();

        String ak = rocketMQProperties.getProducer().getAccessKey();
        String sk = rocketMQProperties.getProducer().getSecretKey();
        boolean isEnableMsgTrace = rocketMQProperties.getProducer().isEnableMsgTrace();
        String customizedTraceTopic = rocketMQProperties.getProducer().getCustomizedTraceTopic();

        DefaultMQProducer producer = RocketMQUtil.createDefaultMQProducer(groupName, ak, sk, isEnableMsgTrace, customizedTraceTopic);

        producer.setNamesrvAddr(nameServer);
        if (!StringUtils.isEmpty(accessChannel)) {
            producer.setAccessChannel(AccessChannel.valueOf(accessChannel));
        }
        producer.setSendMsgTimeout(producerConfig.getSendMessageTimeout());
        producer.setRetryTimesWhenSendFailed(producerConfig.getRetryTimesWhenSendFailed());
        producer.setRetryTimesWhenSendAsyncFailed(producerConfig.getRetryTimesWhenSendAsyncFailed());
        producer.setMaxMessageSize(producerConfig.getMaxMessageSize());
        producer.setCompressMsgBodyOverHowmuch(producerConfig.getCompressMessageBodyThreshold());
        producer.setRetryAnotherBrokerWhenNotStoreOK(producerConfig.isRetryNextServer());

        return producer;
    }

    @Bean(destroyMethod = "destroy")
    @ConditionalOnBean(DefaultMQProducer.class)
    @ConditionalOnMissingBean(name = ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME)
    public RocketMQTemplate rocketMQTemplate(DefaultMQProducer mqProducer,
        RocketMQMessageConverter rocketMQMessageConverter) {
        RocketMQTemplate rocketMQTemplate = new RocketMQTemplate();
        rocketMQTemplate.setProducer(mqProducer);
        rocketMQTemplate.setMessageConverter(rocketMQMessageConverter.getMessageConverter());
        return rocketMQTemplate;
    }
}

重点 是import注解 引入的这几个配置类。 首先第一个RocketMQTransactionConfiguration配置类,它是来解析RocketMQMessageListener 注解修饰的业务逻辑类的。想要知道RocketMQMessageListener 具体实现方式,就要阅读此类的代码。

java 复制代码
@Configuration
// 此处实现ApplicationContextAware是为了获取容器上下文, 实现SmartInitializingSingleton是为了在单例bean全部初始化后的回调中做一些处理
public class ListenerContainerConfiguration implements ApplicationContextAware, SmartInitializingSingleton {
    ...

    public ListenerContainerConfiguration(RocketMQMessageConverter rocketMQMessageConverter,
        ...
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = (ConfigurableApplicationContext) applicationContext;
    }

    /**
    ** 此处是重点
    */
    @Override
    public void afterSingletonsInstantiated() {
        // 获取容器中所有RocketMQMessageListener注解修饰的bean,并且不是代理对象。
        Map<String, Object> beans = this.applicationContext.getBeansWithAnnotation(RocketMQMessageListener.class)
            .entrySet().stream().filter(entry -> !ScopedProxyUtils.isScopedTarget(entry.getKey()))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        // 循环处理 
        beans.forEach(this::registerContainer);
    }

    private void registerContainer(String beanName, Object bean) {
        Class<?> clazz = AopProxyUtils.ultimateTargetClass(bean);
        ...
        // 获取注解
        RocketMQMessageListener annotation = clazz.getAnnotation(RocketMQMessageListener.class);

        // 解析占位符 获取最终的消费者组的名称
        String consumerGroup = this.environment.resolvePlaceholders(annotation.consumerGroup());
        String topic = this.environment.resolvePlaceholders(annotation.topic());

        // 获取消费者监听器的状态 默认为true
        boolean listenerEnabled =
            (boolean) rocketMQProperties.getConsumer().getListeners().getOrDefault(consumerGroup, Collections.EMPTY_MAP)
                .getOrDefault(topic, true);

        ...
        
        // bean的名字  加后缀区分
        String containerBeanName = String.format("%s_%s", DefaultRocketMQListenerContainer.class.getName(),
            counter.incrementAndGet());
        GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext;
        // 注册监听器
        genericApplicationContext.registerBean(containerBeanName, DefaultRocketMQListenerContainer.class,
            () -> createRocketMQListenerContainer(containerBeanName, bean, annotation));
        DefaultRocketMQListenerContainer container = genericApplicationContext.getBean(containerBeanName,
            DefaultRocketMQListenerContainer.class);
        if (!container.isRunning()) {
            try {
                // 启动监听器
                container.start();
            } catch (Exception e) {
                log.error("Started container failed. {}", container, e);
                throw new RuntimeException(e);
            }
        }

        log.info("Register the listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName);
    }

    ...

   
}

最后进入的start

java 复制代码
public synchronized void start() throws MQClientException {
        switch (this.serviceState) {
            //状态的处理
        }
        // 当订阅改变的时候 需要更新topic订阅信息
        this.updateTopicSubscribeInfoWhenSubscriptionChanged();
        // 检查客户端是否已经在 Broker 中注册
        this.mQClientFactory.checkClientInBroker();
        // 发送心跳包给所有的broker
        this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
        // 触发负载均衡消费者组
        this.mQClientFactory.rebalanceImmediately();
    }

总结上述源码,扫描所有mq的监听器注解,把注解修饰的类注册到容器中,并启动监听。再往深入的start()方法中看,就可以看到消费者、topic、tag相关信息的获取级处理过程,最后是启动。

相关推荐
小江的记录本4 天前
【RocketMQ】RocketMQ核心知识体系全解(5大核心模块:架构模型、事务消息两阶段提交、回查机制、延迟消息、顺序消息)
linux·运维·服务器·前端·后端·架构·rocketmq
__土块__5 天前
一次支付清结算系统线程池故障复盘:从任务积压到异步解耦的架构演进
java·消息队列·rocketmq·线程池·支付系统·故障复盘·异步架构
-南帝-6 天前
RocketMQ2.3.5+SpringBoot 3.2.11+ java17安装-集成-测试案例
java·spring boot·rocketmq
zs宝来了6 天前
RocketMQ 存储原理:CommitLog 与 ConsumeQueue 设计
rocketmq·存储·commitlog·consumequeue
饺子大魔王的男人7 天前
Linux 下 Apache RocketMQ 部署与公网访问实现指南
linux·apache·rocketmq
qq_40999093?11 天前
消息中间件:RabbitMQ、RocketMQ、Kafka快速上手
kafka·rabbitmq·rocketmq
羑悻的小杀马特13 天前
从入门到稳定运行:RocketMQ 5.3.4 单机版安装与开机自启配置详解
运维·服务器·rocketmq
阿里云云原生15 天前
Agent 语音交互如何更稳、更快?一次高并发消息链路优化实践
rocketmq
狼与自由15 天前
RocketMQ 如何保证消息不被重复消费
rocketmq