深入浅出Spring Boot整合RocketMQ

本文围绕RocketMQ展开,先介绍快速上手步骤,包括添加依赖、配置文件、实现消费者和发送者等,完成简单消息收发。接着进入进阶部分,分析RocketMQAutoConfiguration配置顺序、producer.group缺失报错原因,还探讨消费消息策略、注解监听队列、发送顺序消息等内容及相关问题。

前言

安装教程 深入浅出RocketMQ安装和部署

快速上手

1.添加依赖

第一个是原生依赖,第二个是spring-boot-starter,这里我们添加第二个依赖。

<!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-client -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.9.0</version>
        </dependency>
        
 <!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-spring-boot-starter -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>

在rocketmq-spring-boot-starter中已经包含rocketmq-client依赖
在这里插入图片描述

2.配置文件properties

//1
rocketmq.name-server=127.0.0.1:9876
//2
rocketmq.producer.group=provider

生产者配置两条,消费者只需要配置第一条。生产者缺失第二条会报错,错误原因后面会分析。

3.实现消费者

写一个消费消息的实现类,这里我们接受消息来删除Redis中的一个key。

@Service
@RocketMQMessageListener(consumerGroup = RedisKeyListener.GROUP,
        topic = RedisKeyListener.TOPIC,
        consumeMode = ConsumeMode.ORDERLY)
public class RedisKeyListener implements RocketMQListener<String> {
   
   

    public static final String GROUP = "redis_group";
    public static final String TOPIC = "redis_topic";

    private static final Logger logger = LoggerFactory.getLogger(RedisKeyListener.class);

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public void onMessage(String key) {
   
   
        logger.info("redis consumer work for key : {} ", key);
        stringRedisTemplate.delete(key);
    }
}

可以通过注解中的nameServer去覆盖配置文件中的值。

4.实现发送者

写一个简单的生产者

@RequestMapping("redis")
@RestController
public class RedisKeyController {
   
   

    private static final Logger logger = LoggerFactory.getLogger(RedisKeyController.class);

    @Autowired
    RocketMQTemplate template;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @GetMapping("put")
    public void putKey(String key, String value) {
   
   
        stringRedisTemplate.opsForValue().set(key, value);
    }

    @GetMapping("delete")
    public void delete(String key) {
   
   
        logger.info("key : {} is send to MQ ", key);
        try {
   
   
            template.convertAndSend(RedisConstant.TOPIC, key);
        } catch (MessagingException e) {
   
   
            e.printStackTrace();
        }
    }
}

控制台输出

c.e.r.s.b.p.c.RedisKeyController         : key : 1 is send to MQ 
c.e.r.s.b.c.consumer.RedisKeyListener    : redis consumer work for key : 1 

7.可视化界面效果图

在这里插入图片描述
详情
在这里插入图片描述
点击重复消费,后台日志会出现两条记录。

c.e.r.s.b.c.consumer.RedisKeyListener    : redis consumer work for key : 1 
c.e.r.s.b.c.consumer.RedisKeyListener    : redis consumer work for key : 1 

这样我们就已经完成了一个简单的消息收发的过程。

进阶

RocketMQAutoConfiguration中的After和Before

这几个配置类谁先谁后?

@AutoConfigureAfter({
   
   MessageConverterConfiguration.class})
@AutoConfigureBefore({
   
   RocketMQTransactionConfiguration.class})

MessageConverterConfiguration -> RocketMQAutoConfiguration -> RocketMQTransactionConfiguration,
after和before注解在自定义starter中已经分析过,这里不再赘述。

rocketmq.producer.group缺失报错原因分析

之前我们在配置生产者的时候,如果将注释1下面的代码去掉会导致一个报错。

rocketmq:
  name-server: ip:端口
  //1
  producer:
    group: rocketMQ

报错提示

Field template in com.example.rocketmqspringboot.controller.MessageSendController required a bean of type 'org.apache.rocketmq.spring.core.RocketMQTemplate' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)

根据提示可知,容器中没有RocketMQTemplate,我们找到自动配置类 RocketMQAutoConfiguration ,搜索RocketMQTemplate 后找到这么一段代码

    @Bean(destroyMethod = "destroy")
    @Conditional(ProducerOrConsumerPropertyCondition.class)
    @ConditionalOnMissingBean(name = ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME)
    public RocketMQTemplate rocketMQTemplate(RocketMQMessageConverter rocketMQMessageConverter) {
   
   
        RocketMQTemplate rocketMQTemplate = new RocketMQTemplate();
        if (applicationContext.containsBean(PRODUCER_BEAN_NAME)) {
   
   
            rocketMQTemplate.setProducer((DefaultMQProducer) applicationContext.getBean(PRODUCER_BEAN_NAME));
        }
        if (applicationContext.containsBean(CONSUMER_BEAN_NAME)) {
   
   
            rocketMQTemplate.setConsumer((DefaultLitePullConsumer) applicationContext.getBean(CONSUMER_BEAN_NAME));
        }
        rocketMQTemplate.setMessageConverter(rocketMQMessageConverter.getMessageConverter());
        return rocketMQTemplate;
    }

由于代码不会返回null,说明能进到这段代码,RocketMQTemplate 一定会被创建,接下来关注这两个条件注解。
ConditionalOnMissingBean是容器没有name=rocketMQTemplate时候为true,所以问题来自@Conditional

@Conditional(ProducerOrConsumerPropertyCondition.class)

ProducerOrConsumerPropertyCondition 源码如下

    static class ProducerOrConsumerPropertyCondition extends AnyNestedCondition {
   
   

        public ProducerOrConsumerPropertyCondition() {
   
   
            super(ConfigurationPhase.REGISTER_BEAN);
        }

        @ConditionalOnBean(DefaultMQProducer.class)
        static class DefaultMQProducerExistsCondition {
   
   
        }

        @ConditionalOnBean(DefaultLitePullConsumer.class)
        static class DefaultLitePullConsumerExistsCondition {
   
   
        }
    }

AnyNestedCondition

Any:任何、任意,Nested:嵌套,Condition: 条件

Can be used to create composite conditions. 可用于创建复合条件

在ProducerOrConsumerPropertyCondition 这个条件中,DefaultMQProducer和DefaultLitePullConsumer同时返回true,AnyNestedCondition 才会为真,相当于 && 。可以理解为下面这种形式。

    @ConditionalOnBean(DefaultMQProducer.class)
    @ConditionalOnBean(DefaultLitePullConsumer.class)

如果这么拆分会产生注解重复问题,所

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值