Springboot集成rocketmq和rabbitmq消息队列(可配置)
SpringBoot集成RocketMQ和RabbitMQ消息队列(可配置)
简介
在SpringBoot项目中集成消息是一个常见的需求,特别是在需要处理高并发、高性能的消息场景下。RocketMQ是一个分布式消息传递和流计算平台,具有高吞吐量、高可用性、可扩展性等特点。网上比较多的都是集成某一种单一消息队列,下面将介绍如何在SpringBoot项目中通过配置的方式集成RocketMQ和RabbitMQ。
步骤一:添加依赖
首先,你需要在你的SpringBoot项目的pom.xml文件中添加RocketMQ和RabbitMQ的依赖。如果你使用的是Maven,可以添加如下依赖:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
步骤二:配置RocketMQ和RabbitMQ
在application.properties或application.yml文件中配置RocketMQ和RabbitMQ的相关属性。例如:
spring:
# 开关,指定使用mq rocketmq rabbitmq no-mq(不使用mq)
mq: rabbitmq
rabbitmq:
host: localhost
username: admin
password: password
virtual-host: /
publisher-confirms: true
port: 5672
autoconfigure:
exclude: org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
rocketmq:
name-server: localhost:9876
producer:
#生产者组名,规定在一个应用里面必须时唯一的(这里直接引用项目名称,或者自定义唯一组名)。
group: my-producer-group
#消息发送的超时时间,毫米级别,默认为3S
send-message-timeout: 3000
#消息达到4096字节的时候,消息就会被压缩。默认就是4096,有利于网络传输,提升性能。
compress-message-body-threshold: 4096
#最大的消息限制 默认为128K
max-message-size: 4194304
#同步消息发送失败重试次数
retry-times-when-send-failed: 3
#在内部发送失败时是否重试其他代理。 源码:setRetryAnotherBrokerWhenNotStoreOK,就是指:发送到broker-a失败是否发送到broker-b。这个参数在有多个broker才生效。
retry-next-server: true
#异步消息发送失败重试的次数
retry-times-when-send-async-failed: 3
配置RabbitMQ的Configuration,使用ConditionalOnProperty注解判断是否启用加载
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
@Configuration
@ConditionalOnProperty(name="spring.mq",havingValue = "rabbitmq")
public class MQConfiguration {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.rabbitmq")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
//设置序列化规则
template.setMessageConverter(new Jackson2JsonMessageConverter());
return template;
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
//SimpleRabbitListenerContainerFactory发现消息中有content_type有text就会默认将其转换成string类型的
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setPrefetchCount(1);
//针对每个监听器,设置最小的并发消费者的数量,一次性从队列取5条消息,开5个线程同时处理任务
factory.setConcurrentConsumers(1);
//设置最大的并发的消费者数量
factory.setMaxConcurrentConsumers(1);
//设置反序列化规则
factory.setMessageConverter(new Jackson2JsonMessageConverter());
//设置确认模式手工确认, 开启手动 ack
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//投递失败时是否重新排队
factory.setDefaultRequeueRejected(false);
configurer.configure(factory, connectionFactory);
return factory;
}
}
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(name="spring.mq",havingValue = "rabbitmq")
public class MyRabbitAutoConfiguration extends RabbitAutoConfiguration {
}
步骤三:创建消息生产者
在Spring Boot应用中创建消息生产者用于消息发送,这里要注意的是需要创建一个消息发送工具类,消息发送接口类,一个RabbitMQ消息发送实现类,一个RocketMQ消息发送实现类和一个默认消息不发送实现类:
消息发送工具类:
package com.seeingtv.vas.core.mq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @ClassName: SendMsgHandler
* @Description: mq消息生产者
* @author:
* @date: 2019年3月18日 下午7:13:14
*/
@Component
public class SendMsgHandler {
private static final Logger log = LoggerFactory.getLogger(SendMsgHandler.class);
@Autowired
private IMqTemplate mqTemplate;
/**
* @return boolean
* @Author
* @Description 发送mq消息
* @Date 15:44 2019/7/19
* @Param [queueName, datajson]
**/
public boolean sendMqMsg(String queueName, String datajson) {
return mqTemplate.convertAndSend(queueName,datajson);
}
/**
* @return boolean
* @Author
* @Description 发送mq消息
* @Date 15:44 2019/7/19
* @Param [queueName, datajson]
**/
public boolean sendMqMsg(String queueName, String datajson, int priority) {
return mqTemplate.convertAndSend(queueName,datajson,priority);
}
}
消息发送接口类:
public interface IMqTemplate {
boolean convertAndSend(String queueName, String datajson);
boolean convertAndSend(String queueName, String datajson, int priority);
}
RabbitMQ消息发送实现类:
import com.alibaba.fastjson.JSONObject;
import com.seeingtv.vas.common.constant.MqConstants;
import com.seeingtv.vas.common.utils.Fastjson2Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@Component
@ConditionalOnProperty(name="spring.mq",havingValue = "rabbitmq")
public class RabbitMqTemplateImpl implements IMqTemplate{
private static final Logger log = LoggerFactory.getLogger(RabbitMqTemplateImpl.class);
@Autowired
private AmqpTemplate amqpTemplate;
@Override
public boolean convertAndSend(String queueName, String datajson) {
try {
JSONObject jsonObject = Fastjson2Utils.jsonToObject(datajson,JSONObject.class);
amqpTemplate.convertAndSend(MqConstants.exchange,queueName+MqConstants.routingKey, jsonObject);
return true;
} catch (Exception e) {
log.error("发送mq消息失败队列名称:{},mq消息:{},优先级:{}", queueName, datajson, e);
}
return false;
}
@Override
public boolean convertAndSend(String queueName, String datajson, int priority) {
try {
JSONObject jsonObject = Fastjson2Utils.jsonToObject(datajson,JSONObject.class);
amqpTemplate.convertAndSend(MqConstants.exchange,queueName+MqConstants.routingKey, jsonObject, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setPriority(priority);
return message;
}
});
return true;
} catch (AmqpException e) {
log.error("发送mq消息失败队列名称:{},mq消息:{},优先级:{}", queueName, datajson, priority, e);
}
return false;
}
}
RocketMQ消息发送实现类:
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@Component
@ConditionalOnProperty(name = "spring.mq", havingValue = "rocketmq")
public class RocketMqTemplateImpl implements IMqTemplate {
private static final Logger log = LoggerFactory.getLogger(RocketMqTemplateImpl.class);
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public boolean convertAndSend(String queueName, String dataJson) {
boolean flag = false;
try {
SendResult sendResult = rocketMQTemplate.syncSend(queueName, dataJson);
flag = sendResult.getSendStatus().equals(SendStatus.SEND_OK);
} catch (Exception e) {
log.error("mq发送消息失败", e);
}
return flag;
}
@Override
public boolean convertAndSend(String queueName, String dataJson, int priority) {
return convertAndSend(queueName, dataJson);
}
}
默认消息不发送实现类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@Component
@ConditionalOnProperty(name="spring.mq",havingValue = "no-mq", matchIfMissing = true)
public class NoMqTemplateImpl implements IMqTemplate{
private static final Logger log = LoggerFactory.getLogger(NoMqTemplateImpl.class);
@Override
public boolean convertAndSend(String queueName, String datajson) {
return false;
}
/**
* @description:
* @author:
* @date: 2022/10/28 14:18
* @param: [queueName, datajson, priority]
* @return: boolean
**/
@Override
public boolean convertAndSend(String queueName, String datajson, int priority) {
return false;
}
}
至此已经可以通过配置文件中spring.mq来配置使用哪个mq来发送消息了
步骤四:创建消费者消费mq
RocketMQ消费者实现:
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;
@ConditionalOnProperty(name="spring.mq",havingValue = "rocketmq")
@Service
@RocketMQMessageListener(topic = "your-topic", consumerGroup = "my-consumer-group")
public class MessageConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("Received message: " + message);
}
}
RabbitMQ消费者实现:
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import java.io.IOException;
@ConditionalOnProperty(name = "spring.mq", havingValue = "rabbitmq")
@Component
@RabbitListener(bindings = @QueueBinding(value = @Queue("your-topic"),
exchange = @Exchange("your-exchange"),
key = ("your-topic" + ".routingKey")))
public class MessageConsumer {
private static final Logger log = LoggerFactory.getLogger(MessageConsumer.class);
@RabbitHandler
public void onMessage(@Payload Object o, Message msg, Channel channel) {
try {
System.out.println("Received message: " + o);
} catch (Exception e) {
log.error("MessageConsumer error!", e);
} finally {
try {
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
log.error("MessageConsumer error", e);
}
}
}
}
小结
实现的技术细节就是springboot的一个条件注解ConditionalOnProperty的使用。
ConditionalOnProperty注解是Spring Boot中的一个条件注解,主要用于根据配置文件中的属性值来决定是否创建或注入某个Bean。这个注解通过其属性name和havingValue来控制条件是否生效。
ConditionalOnProperty注解常用于以下场景:
条件创建Bean:在某些情况下,可能需要根据配置文件中的属性值来决定是否创建某个Bean。例如,如果配置文件中没有指定某个属性,则不创建相应的Bean。
条件注入Bean:在有多个实现类的情况下,可以通过ConditionalOnProperty注解配合配置文件来决定注入哪个实现类。例如,如果配置文件中指定了某个实现类,则注入该实现类;否则,注入另一个实现类。
基本用法:在@Configuration注解的类上使用@ConditionalOnProperty注解,可以控制该配置类是否生效。如果配置文件中的属性值与注解中的havingValue值相等,则该配置类生效;否则,不生效。
属性说明:
name:指定要检查的配置项的名称。
havingValue:指定配置项的期望值。如果配置项的值与havingValue相等,则条件成立;否则,条件不成立。
matchIfMissing:如果为true,即使配置项不存在,条件也会成立;否则,条件不成立。
以上案列只是使用RocketMQ和RabbitMQ作为消息队列配置化使用案例,小伙伴们可以根据自己需求引入kafka等其他消息队列
技术不易 蹒跚学步 众志成城 发扬光大
更多推荐



所有评论(0)