(二)RocketMq与Spring的集成开发原理之传统Spring和Springboot方式

本文介绍RocketMQ在传统Spring项目和Spring Boot项目中的集成方法,包括Maven配置、消息发送与接收等核心步骤,并深入探讨Spring Boot自动集成RocketMQ的原理。

1.前话

  目前使用RocketMq最主流的无非就三种方式:

  1. 直接使用RocketMq客户端jar包的ProducerConsumer
  2. 对接传统Spring容器,将ProducerConsumer交给Spring容器进行管理;
  3. 使用RocketMq-Springboot-Starter包快速配置集成到Springboot项目。

  对于第一种直接使用RocketMq客户端的方式,这里便不做介绍了,直接跳转至RocketMqGithub文档观看官方给的样例即可。

  这里主要介绍的是传统的Spring项目和Springboot项目集成RocketMq的最佳实践,以及扩展性的简单说明一下Springboot集成的原理。

2.传统Spring集成

  对于传统Spring项目而言,要集成RocketMq和直接使用客户端的ProducerConsumer是差不了太多的,无非是初始化了对象后交给Spring容器进行管理,后续需要使用的时候直接从Spring容器中取出来即可。

2.1 Maven配置

  RocketMq客户端jar包配置如下:

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-acl</artifactId>
    <version>4.5.2</version>
</dependency>

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.5.2</version>
</dependency>

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-common</artifactId>
    <version>4.5.2</version>
</dependency>

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-logging</artifactId>
    <version>4.5.2</version>
</dependency>

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-remoting</artifactId>
    <version>4.5.2</version>
</dependency>

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-srvutil</artifactId>
    <version>4.5.2</version>
</dependency>

2.2 Configuration配置

  将需要使用的ProducerConsumer统一放到Configuration类中进行管理,代码如下:

@Configuration
public class RocketMqConfiguration {

    @Value("${rocketMq.nameSrvAddr}")
    private String nameSrvAddr;

    @Value("${rocketMq.producerGroup}")
    private String producerGroup;

    /**
     * 配置Producer交给Spring进行管理
     *
     * @return
     */
    @Bean(initMethod = "start", destroyMethod = "shutdown")
    public DefaultMQProducer defaultMqProducer() {
        // 使用默认的MQ producerGroup分组
        DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
        // 设置NameServer地址
        producer.setNamesrvAddr(nameSrvAddr);
        // 其它属性看需求
        return producer;
    }

    @Value("${rocketMq.consumerGroup}")
    private String consumerGroup;

    @Value("${rocketMq.consumerTopic}")
    private String consumerTopic;

    @Value("${rocketMq.consumerTag}")
    private String consumerTag;

    /**
     * 注册默认的Consumer
     *
     * @param messageListenerConcurrently
     * @return
     * @throws MQClientException
     */
    @Bean(initMethod = "start", destroyMethod ="shutdown")
    public DefaultMQPushConsumer defaultMqPushConsumer(
            @Qualifier("generalConcurrentlyListener")MessageListenerConcurrently messageListenerConcurrently)
            throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
        // 设置NameServer地址
        consumer.setNamesrvAddr(nameSrvAddr);
        // 订阅topic和tag
        consumer.subscribe(consumerTopic, consumerTag);
        // 注册listener监听器
        consumer.registerMessageListener(messageListenerConcurrently);
        // 其它属性配置看需求
        return consumer;
    }

}

  配置方式和直接使用初始化使用差不多,只是把初始化后的ProducerConsumer交给了Spring容器进行管理,具体的RocketMq属性再配置到Spring容器中即可。

2.3 配置MessageListener监听器

  在Configuration类中是引用了MessageListener监听类,因此需要在Spring容器中创建一个名为generalConcurrentlyListenerBean,代码如下:

@Slf4j
@Component("generalConcurrentlyListener")
public class GeneralConcurrentlyListener implements MessageListenerConcurrently {

    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        for (Message m : msgs) {
            log.info("timestamp:{} msg body:{} msg data:{}", System.currentTimeMillis(), new String(m.getBody()), m);
        }
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
}

  一个很简单的实现,只需要确保这个Bean交给Spring容器管理即可,到这里就能保证Consumer是可以正常消费TopicTag的消息了。

2.4 使用Producer

  如果要发送消息只需要从Spring容器中获取Producer,再使用对象进行相应的发消息操作即可,这里是使用了最简单的方式,代码如下:

@Slf4j
@Service
public class SendMqService {

    @Value("${rocketMq.consumerTopic}")
    private String consumerTopic;

    @Value("${rocketMq.consumerTag}")
    private String consumerTag;

    @Autowired
    private DefaultMQProducer producer;

    /**
     * 发送mq消息
     *
     * @param messageBody
     */
    public void sendMq(String messageBody) {
        Message message = new Message(consumerTopic, consumerTag, messageBody.getBytes());
        try {
            // 具体发送什么类型的消息看需求
            SendResult sendResult = producer.send(message);
            log.info("sendResult:{}", sendResult);
        } catch (Exception e) {
            log.error("发送mq消息失败", e);
        }
    }

}

  直接调用sendMq()方法即可发送普通的RocketMq消息,如果要自定义不同的消息只需要添加不同的方法即可。

  Spring简单集成RocketMq只需要配置这几步,使用方式和RocketMqGithub使用文档一样。确保Spring容器有如下几个属性:

rocketMq.nameSrvAddr=http://***
rocketMq.producerGroup=GID_DEV_SPRINGBOOT_DEMO
rocketMq.consumerGroup=GID_DEV_SPRINGBOOT_DEMO
rocketMq.consumerTopic=Hello-Test
rocketMq.consumerTag=TAG_DEV_HELLO_WORLD

3.SpringBoot快速集成

  一般而言,使用较广的框架都会针对Springboot出一个Starter包以方便开发快速集成,RocketMq同理。

3.1 Maven配置

  SpringbootStarter包配置如下:

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>

3.2 Yaml配置

rocketmq:
  name-server: http://***
  producer:
    group: GID_DEV_SPRINGBOOT_DEMO

3.3 MessageListener配置

  如果有消费者则需要配置MessageLisenter监听器来监听队列消息,代码如下:

@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = "GID_DEV_SPRINGBOOT_DEMO", 
        topic = "Hello-Test", selectorExpression = "TAG_DEV_HELLO_WORLD",
        messageModel = MessageModel.CLUSTERING)
public class ClusterMqListener implements RocketMQListener<String> {

    @Override
    public void onMessage(String message) {
        log.info("clustering consume message: {}", message);
    }
}

  consumerGrouptopicselectorExpression都可以将属性配置在Spring容器中再通过${}通配符去获取。

  注:实现类一定要实现RocketMQListener接口,否则会抛异常

3.4 使用RocketMQTemplate模板类发送

  RocketMqSpringboot包提供了发送消息模板类RocketMQTemplate,这个类提供了如下发送方式的快捷方法:

  1. syncSend():同步发送消息;
  2. syncSendOrderly():同步发送严格顺序消息,默认使用的SelectorSelectMessageQueueByHash
  3. asyncSend():异步发送,但必须要填入SendCallboack回调实现类;
  4. asyncSendOrderly:异步发送严格顺序消息,同样需要填入回调实现类,默认使用的SelectorSelectMessageQueueByHash
  5. sendOneWay():单方发送消息;
  6. sendOneWayOrderly():单方发送严格顺序消息,默认使用的SelectorSelectMessageQueueByHash

  通用的方法大概有这六大类,事务性的用得少便不写上去了,这六大类快捷发送消息方法将一些通用流程都封装到了方法中,如果没有特殊的需求直接使用这些模板方法可以节省一定的开发时间。使用代码如下:

@Slf4j
@Service
public class SendMqService {

    @Value("${rocketMq.consumerTopicTag:Hello-Test:TAG_DEV_HELLO_WORLD}")
    private String topicTag;

    @Autowired
    private RocketMQTemplate rocketMqTemplate;

    /**
     * 发送mq消息
     *
     * @param messageBody
     */
    public void sendMq(String messageBody) {
        // 具体发送什么类型的消息看需求
        SendResult sendResult = rocketMqTemplate.syncSend(topicTag, content);
        log.info("sendResult:{}", sendResult);
    }

}

  相较于直接使用DefaultMQProducer类去自己封装消息体及确定各种对象,使用模板类发送更为简单,如有特殊需求同样可以参考传统Spring那种使用方式,直接获取DefaultMQProducer类,再自己封装消息体和发送方式。

  这种配置使用方式个人是十分推荐的,快捷方便,简单直接,基本能覆盖大部分的使用场景,但这种使用方式并没有很常见,更多的还是传统Spring的那种配置方式。


4.Springboot自动集成的原理

4.1 RocketMq的自动装填类

  只要稍微熟悉Springboot自动装填机制,就可以知道RocketMq一定是写了个AutoConfiguration类去嵌入Springboot,从rocketmq-spring-bootjar包中就很容易看到RocketMQAutoConfiguration类,这个类就是自动集成的核心了。

  这个类做的事也很简单,就是把要集成到Spring框架的事情在这个类中帮我们做了,其源码如下:

@Configuration
public class RocketMQAutoConfiguration {

    @Autowired
    private Environment environment;

    @Bean
    public DefaultMQProducer defaultMQProducer(RocketMQProperties rocketMQProperties) {
        // **配置略过,感兴趣的可以自行去看看
        DefaultMQProducer producer;
        // **配置略过,感兴趣的可以自行去看看
        return producer;
    }

    @Bean(destroyMethod = "destroy")
    public RocketMQTemplate rocketMQTemplate(DefaultMQProducer mqProducer,
        ObjectMapper rocketMQMessageObjectMapper,
        RocketMQMessageConverter rocketMQMessageConverter) {
        RocketMQTemplate rocketMQTemplate = new RocketMQTemplate();
        // **配置略过,感兴趣的可以自行去看看
        return rocketMQTemplate;
    }

}

  贴出来的代码省略了很多,但里面大致的关键要素代码就这几段,是不是和传统的Spring配置方式相差不大?

4.2 Producer的启动

  看完上段代码是知道了自动装填类帮我们把DefaultMQProducerRocketMQTemplate类注册到了Spring容器中,但并没有看到调用Producerstart()方法,那么Producer启动是在那里完成的呢?请看到RocketMQTemplate类的关键源码:

public class RocketMQTemplate extends AbstractMessageSendingTemplate<String> 
                        implements InitializingBean, DisposableBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        if (producer != null) {
            producer.start();
        }
    }
    @Override
    public void destroy() {
        if (Objects.nonNull(producer)) {
            producer.shutdown();
        }
        for (Map.Entry<String, TransactionMQProducer> kv : cache.entrySet()) {
            if (Objects.nonNull(kv.getValue())) {
                kv.getValue().shutdown();
            }
        }
        cache.clear();
    }
}

  看到这个类实现了InitializingBeanDisposableBean接口是不是就豁然开朗了?原来Producer是借助RocketMQTemplate还魂了。

4.3 MessageListener的读取解析

  那么疑问又来了,Consumer是在哪里创建监听消息的呢?这就需要看到RocketMQAutoConfiguration类中的一段关键代码了:

@Import({ListenerContainerConfiguration.class})
public class RocketMQAutoConfiguration {
}

  在RocketMQAutoConfiguration类中引入了ListenerContainerConfiguration类,在这个类中将会完成对@RocketMQMessageListener注解的解析并生成对应的Consumer,其关键源码如下:

@Configuration
public class ListenerContainerConfiguration implements
                SmartInitializingSingleton {
    @Override
    public void afterSingletonsInstantiated() {
        // 获取Spring容器中的所有被@RocketMQMessageListener注解的bean
        Map<String, Object> beans = this.applicationContext
                .getBeansWithAnnotation(RocketMQMessageListener.class);
        // 如果有则依次将bean传进registerContainer()方法执行
        if (Objects.nonNull(beans)) {
            beans.forEach(this::registerContainer);
        }
    }
    private void registerContainer(String beanName, Object bean) {
        // 不重要代码略过,感兴趣的可以自行去看看
        // 从bean获取@RocketMQMessageListener注解信息
        RocketMQMessageListener annotation = clazz.getAnnotation(RocketMQMessageListener.class);

        // 使用placeholder类解析注解的属性,这就是为什么可以在注解中
        // 使用${}通配符
        String consumerGroup = this.environment
                .resolvePlaceholders(annotation.consumerGroup());
        String topic = this.environment
                .resolvePlaceholders(annotation.topic());
                
        // 不重要代码略过,感兴趣的可以自行去看看

        // createRocketMQListenerContainer()方法会根据注解的其它属性创建
        // DefaultRocketMQListenerContainer类,有兴趣可以自行观看
        genericApplicationContext.registerBean(containerBeanName, DefaultRocketMQListenerContainer.class,
            () -> createRocketMQListenerContainer(containerBeanName, bean, annotation));
        // 这里直接使用Spring上下文类的getBean()方法,此时将会在容器内
        // 实例化刚刚创建的DefaultRocketMQListenerContainer类
        DefaultRocketMQListenerContainer container = genericApplicationContext.getBean(containerBeanName,
            DefaultRocketMQListenerContainer.class);
        if (!container.isRunning()) {
            try {
                // 如果容器尚未执行则调用start()方法,这里面将会调用
                // Consumer的start()方法启动消费者
                container.start();
            } catch (Exception e) {
                // 不重要代码略过,感兴趣的可以自行去看看
            }
        }
        // 不重要代码略过,感兴趣的可以自行去看看
    }
}

  简要说明一下这个类的执行流程和作用:

  1. RocketMQAutoConfiguration被自动装填到Spring容器后,将会使用@Import注解引入ListenerContainerConfiguration
  2. 当被引入的ListenerContainerConfiguration类被Spring容器实例化后将会执行afterSingletonsInstantiated方法;
  3. 在该方法中将会从Spring容器中取出所有被RocketMQMessageListener注解过的Bean
  4. BeanDefaultRocketMQListenerContainer类封装,并注册到Spring容器中实例化;
  5. 调用DefaultRocketMQListenerContainerstart()方法,启动消费者。

  总而言之就是实现了RocketMQListener接口且被RocketMQMessageListener注解的Bean,最终会被封装成DefaultRocketMQListenerContainer类,在这里面的Consumer消费消息,并经过一系列处理(诸如转换消息内容)最终调用到Bean的接口方法实现中。

  这样是不是就清晰多了?如果感兴趣推荐自行去看源码,很多疑惑将会在源码中给出答案。

<think>首先,用户的问题是:“我想比较SpringSpring Boot集成RocketMQ时的差异 请问Spring框架Spring Boot框架集成RocketMQ的区别”。我需要基于系统级指令来构建我的回答。 系统级指令关键点: - 所有行内数学表达式用$...$格式。 - 独立公式用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 参考提供的站内引用:[^1][^2]。 提供的引用: - [^1]: 讨论了Spring Boot整合RocketMQ的场景,如需要更多自定义控制、现有项目集成、团队专业知识。 - [^2]: 展示了Spring Boot中使用rocketMQTemplate发送消息的代码示例。 用户的问题是比较Spring框架Spring Boot框架在集成RocketMQ时的差异。所以,我需要解释两者的区别。 关键区别: - **Spring框架**:是基础框架,集成RocketMQ需要手动配置,如定义Bean、设置连接工厂等。更灵活但更繁琐。 - **Spring Boot**:基于Spring,提供自动配置starter依赖,简化了集成。使用如spring-boot-starter-rocketmq依赖,自动配置连接。 基于引用[^1],它强调了Spring Boot整合的优势:自定义控制、现有项目集成、团队专业知识。 我的回答结构: 1. **引言**:简要介绍主题。 2. **逐步比较**: - 配置方式Spring需要手动配置;Spring Boot有自动配置。 - 依赖管理:Spring Boot使用starter依赖简化。 - 自定义程度:Spring更灵活;Spring Boot更便捷。 - 代码示例:引用[^2]展示Spring Boot代码。 3. **总结差异**:列出主要区别。 4. **生成相关问题**:基于主题生成几个问题。 确保LaTeX格式:由于主题是框架集成,可能没有数学表达式,但如果涉及,就用$...$。例如,如果有公式,但这里不太可能。 使用中文回答。 添加引用:在相关部分末尾添加[^1]或[^2]。 生成相关问题:在回答末尾,添加“相关问题”部分,类似示例。 完整回答结构: - 开头:直接回答问题。 - 主体:分点解释差异。 - 结尾:总结。 - 相关问题:列出2-3个问题。 参考用户上一次问题:用户说“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题你的回答。”但这是第一次交互,所以没有上一次问题。直接基于当前问题。 最终回答大纲: 1. 介绍SpringSpring Boot集成RocketMQ的背景。 2. 比较差异: - 配置复杂度。 - 依赖管理。 - 自定义能力。 - 适用场景。 3. 引用提供的引用。 4. 生成相关问题。</think>### Spring框架Spring Boot框架集成RocketMQ的区别 在比较Spring框架Spring Boot框架集成RocketMQ的差异时,核心区别在于**配置方式、依赖管理、自定义灵活性以及开发效率**上。Spring Boot是构建在Spring框架之上的简化框架,它通过自动配置starter依赖大幅简化了集成过程,而Spring框架则需要更多手动工作。以下我将逐步分析两者的关键差异,帮助您清晰理解。 #### 1. **配置方式** - **Spring框架**:需要手动配置所有组件。例如,您必须自行定义RocketMQ的连接工厂、生产者消费者Bean,并在XML或Java配置文件中显式设置参数(如NameServer地址、组名)。这种方式灵活性强,但代码量较大且容易出错。例如: ```java // Spring框架手动配置示例 @Bean public DefaultMQProducer producer() { DefaultMQProducer producer = new DefaultMQProducer("producer_group"); producer.setNamesrvAddr("localhost:9876"); producer.start(); return producer; } ``` 这要求开发者深入理解RocketMQ的API细节,增加了学习曲线[^1]。 - **Spring Boot**:通过自动配置简化集成。只需添加`spring-boot-starter-rocketmq`依赖,Spring Boot会自动配置连接工厂模板类(如`RocketMQTemplate`)。您只需在`application.properties`中设置关键参数(如`rocketmq.name-server=localhost:9876`),无需编写额外代码。例如,发送消息只需一行: ```java // Spring Boot自动配置示例 rocketMQTemplate.convertAndSend("topic:tag", "消息内容"); // 引用自[^2] ``` 这种方式显著提升开发效率,尤其适合快速迭代项目[^1]。 #### 2. **依赖管理** - **Spring框架**:依赖管理较为繁琐。您需要手动添加所有RocketMQ相关库(如`rocketmq-client`)并处理版本兼容问题。在Maven或Gradle中,需显式声明每个依赖,这可能导致依赖冲突或遗漏。 - **Spring Boot**:使用starter依赖一站式解决。例如,添加`spring-boot-starter-rocketmq`会自动引入所有必要库,并确保版本兼容。这减少了配置负担,让开发者更专注于业务逻辑[^1]。 #### 3. **自定义灵活性** - **Spring框架**:提供更高的自定义能力。如果您需要实现复杂逻辑(如事务性消息、自定义消息过滤器或低级别API调用),Spring框架允许您完全控制配置细节。例如,您可以精细调整线程池或重试机制。这适合需要深度优化的场景,但要求团队有较强专业知识[^1]。 - **Spring Boot**:虽然便捷,但在高度自定义场景下可能受限。Spring Boot的自动配置“约定优于配置”原则简化了常见用例,但如果需覆盖默认行为(如修改消息序列化方式),仍需通过`@Bean`手动覆盖配置。不过,对于大多数标准应用(如消息发送/消费),它已足够高效[^2]。 #### 4. **适用场景** - **Spring框架**:更适合**已有Spring项目**或**需要极致自定义**的情况。例如,在遗留系统中集成RocketMQ,或处理特殊需求如消息轨迹追踪。但开发周期较长,维护成本较高。 - **Spring Boot**:推荐用于**新项目**或**快速原型开发**。它简化了部署测试,尤其适合微服务架构。引用[^1]指出,如果团队拥有RocketMQ专业知识且追求效率,Spring Boot是更优选择。反之,如果团队更熟悉底层配置,Spring框架可能更合适。 #### 总结差异表 | 方面 | Spring框架 | Spring Boot | |--------------------|----------------------------------------|------------------------------------------| | 配置复杂度 | 高(手动配置Bean参数) | 低(自动配置,starter依赖) | | 开发效率 | 较低(需更多代码) | 较高(开箱即用) | | 自定义能力 | 强(完全控制细节) | 中等(需覆盖默认配置) | | 适用场景 | 复杂交互、现有项目集成 | 新项目、快速开发、标准消息处理 | | 学习曲线 | 陡峭(需掌握RocketMQ API) | 平缓(简化API如RocketMQTemplate) | 总之,Spring Boot集成RocketMQ时以**便捷性效率**取胜,而Spring框架则提供**更细粒度的控制**。选择时需权衡项目需求:如果追求快速上线标准化,优先Spring Boot;如果需要深度定制或集成到复杂环境,Spring框架更灵活[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值