在springblade框架中集成rabbitmq
创建新模块(blade-rabbitmq)并添加rabbitmq所需依赖
-
blade-rabbitmq服务pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>blade-ops</artifactId> <groupId>org.springblade</groupId> <version>3.6.0</version> </parent> <artifactId>blade-ops-rabbitmq</artifactId> <name>${project.artifactId}</name> <version>${blade.tool.version}</version> <packaging>jar</packaging> <description>springblade集成rabbitmq</description> <dependencies> <!--Blade--> <dependency> <groupId>org.springblade</groupId> <artifactId>blade-core-boot</artifactId> <version>${blade.tool.version}</version> </dependency> <!--rabbitmq--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
创建配置类RabbitMqConfig
-
配置文件
spring: #配置rabbitMq 服务器 rabbitmq: host: 127.0.0.1 port: 5672 username: guest/root password: guest/root
-
声明方式(项目启动即创建完成):
/** * 创建队列1 */ @Bean // @Bean(RabbitMqConstant.QUEUE_NAME_ONE) //bean的名称与队列的名称都为 my-queue-2 不指定则默认为方法名 // @Qualifier public Queue createQueueOne() { return new Queue(RabbitMqConstant.QUEUE_NAME_ONE); } /** * 创建队列2 */ @Bean @Qualifier public Queue createQueueTwo() { //队列名叫 my-queue-2 return new Queue(RabbitMqConstant.QUEUE_NAME_TWO); } /** * 创建 直连交换机 */ @Bean public DirectExchange createDirectExchange() { return new DirectExchange(RabbitMqConstant.DIRECT_EXCHANGE_NAME); } /** * 创建主题交换机 */ @Bean public TopicExchange createTopicExchange() { return new TopicExchange(RabbitMqConstant.TOPIC_EXCHANGE_NAME); } /** * 创建 扇型交换机 */ @Bean public FanoutExchange createFanoutExchange() { return new FanoutExchange(RabbitMqConstant.FANOUT_EXCHANGE_NAME); } /** * 直连交换机绑定队列并指定路由键 */ @Bean public Binding directExchangeBingdingQueue(Queue createQueueOne, DirectExchange exchange) { return BindingBuilder.bind(createQueueOne).to(exchange).with(RabbitMqConstant.DIRECT_ROUTING_KEY); } /** * 主题交换机绑定队列并指定路由键 * 当有多个queue 且属性名 不指定绑定你哪个队列时 可以使用 @Qualifier注解 限定注入哪个queue的bean * 若创建 queue的bean是指定了bean的名字 则需要在@Qualifier注解中 指定限定符,例如:@Qualifier(RabbitMqConstant.QUEUE_NAME_ONE) */ @Bean public Binding topicExchangeBingdingQueue(@Qualifier Queue queue, TopicExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with(RabbitMqConstant.TOPIC_ROUTING_KEY); } /** * 扇型交换机绑定队列 (不需要路由键) */ @Bean public Binding fanoutExchangeBingdingQueue(@Qualifier Queue queue, FanoutExchange exchange) { return BindingBuilder.bind(queue).to(exchange); }
-
动态方式(项目启动时不创建,调用创建方法后再去创建):
@Value("${spring.rabbitmq.host}") private String host; @Value("${spring.rabbitmq.port}") private int port = 5672; @Value("${spring.rabbitmq.username}") private String userName; @Value("${spring.rabbitmq.password}") private String password; /** * 建立rabbitmq连接 */ @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setHost(host); connectionFactory.setPort(port); connectionFactory.setUsername(userName); connectionFactory.setPassword(password); //connectionFactory.setVirtualHost("/"); return connectionFactory; } /** * 构建rabbitAdmin的bean 并交给spring容器管理 * 构建这个是为了动态创建指定类型的交换机和队列(非声明方式创建) */ @Bean public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); //autoStartup必须要设置为true,否则Spring容器不会加载RabbitAdmin类 rabbitAdmin.setAutoStartup(true);// 服务启动时候开启自动启动 return rabbitAdmin; } @Bean @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) //设置bean的作用域,作用域是指实例的生命周期,此处设置为 为单例模式 public RabbitMqQueueTemplate rabbitMqQueueTemplate() { RabbitMqQueueTemplate rabbitMqQueueTemplate = new RabbitMqQueueTemplate(); rabbitMqQueueTemplate.setConnectionFactory(connectionFactory()); //发送消息时设置强制标志 rabbitMqQueueTemplate.setMandatory(true); //为此模板设置消息转换器。用于解析对象参数以转换和发送方法以及来自接收和转换方法的对象结果。 //rabbitMqQueueTemplate.setMessageConverter(); return rabbitMqQueueTemplate; } /** * 消息监听 */ @Bean public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory) { SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer(connectionFactory); return messageListenerContainer; }
常量定义类RabbitMqConstant
//队列名称1
String QUEUE_NAME_ONE = "my-queue-1";
//队列名称2
String QUEUE_NAME_TWO = "my-queue-2";
//直连交换机名称
String DIRECT_EXCHANGE_NAME = "my-direct-exchange";
/**
* 主题交换机名称
*/
String TOPIC_EXCHANGE_NAME = "my-topic-exchange";
//扇形交换机(广播)名称
String FANOUT_EXCHANGE_NAME = "my-fanout-exchange";
//头部交换机
String HEADERS_EXCHANGE_NAME = "my-headers-exchange";
//路由键
String DIRECT_ROUTING_KEY = "my-direct-routing-key";
String TOPIC_ROUTING_KEY = "my-topic-routing-key";
Mq模板扩展类(自定义方法)RabbitMqQueueTemplate
@Resource
private RabbitAdmin rabbitAdmin;
/**
* 创建MQ队列和交换机,并绑定交换机和队列
*
* @param exchangeName 交换机名
* @param queueName 队列名
* @param routingKey 路由键 change为 FANOUT_EXCHANGE 时不需要
* @param exchangeType 交换机类型
*/
public void createQueueAndBindToExchange(String exchangeName, String queueName, String routingKey, ExchangeType exchangeType) {
this.createQueueAndBindToExchange(exchangeName, queueName, routingKey, true, false, false, null, exchangeType);
}
/**
* 创建MQ队列和交换机,并绑定交换机和队列
*
* @param exchangeName 交换机名称
* @param queueName 队列名称
* @param routingKey 路由键
* @param durable 持久化
* @param exclusive 排他
* @param autoDelete 自动删除
* @param arguments 参数
*/
private void createQueueAndBindToExchange(String exchangeName, String queueName, String routingKey, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments, ExchangeType change) {
Exchange exchange = null;
switch (change) {
case TOPIC_EXCHANGE:
exchange = new TopicExchange(exchangeName, true, false);
break;
case DIRECT_EXCHANGE:
exchange = new DirectExchange(exchangeName, true, false);
break;
case FANOUT_EXCHANGE:
exchange = new FanoutExchange(exchangeName, true, false);
break;
default:
exchange = new DirectExchange(exchangeName, true, false);
break;
}
Queue queue = new Queue(queueName, durable, exclusive, autoDelete, arguments);
if (null != exchange) {
//创建交换机,无则创建有则跳过
rabbitAdmin.declareExchange(exchange);
log.debug("交换机: {} 创建完成!", JSONObject.toJSON(exchange));
//创建队列
rabbitAdmin.declareQueue(queue);
log.debug("队列: {} 创建完成!", JSONObject.toJSON(queue));
Binding binding = new Binding(queueName, Binding.DestinationType.QUEUE, exchangeName, routingKey, null);
//创建绑定关系
rabbitAdmin.declareBinding(binding);
log.debug("交换机: {} 队列: {} 绑定完成!", exchangeName, queue.getName());
} else {
rabbitAdmin.declareQueue(queue);
log.debug("队列创建完成!");
}
}
创建生产者MessageProducer
@Autowired
private RabbitMqQueueTemplate rabbitMqQueueTemplate;
/**
* 监听控制
*/
@Autowired
private SimpleMessageListenerContainer messageListenerContainer;
@Autowired
@Qualifier
private MessageListener messageListener;
/**
* 发送消息
*
* @param message 信息内容
* @param routingKey 路由键
* @param exchangeType 交换机类型
*/
public Object sendMessage(String message, String routingKey, ExchangeType exchangeType) {
log.info("指定交换机: {} ,路由键: {} ,发送信息: {}", exchangeType, routingKey, message);
Object result = null;
switch (exchangeType) {
case DIRECT_EXCHANGE:
result = rabbitMqQueueTemplate.convertSendAndReceive(RabbitMqConstant.DIRECT_EXCHANGE_NAME, routingKey, message);
break;
case TOPIC_EXCHANGE:
//主题交换机 路由键 可以使用两个通配符:*(匹配一个单词)和#(匹配多个单词)
result = rabbitMqQueueTemplate.convertSendAndReceive(RabbitMqConstant.TOPIC_EXCHANGE_NAME, routingKey, message);
break;
case FANOUT_EXCHANGE:
//扇型交换机不需要路由键
result = rabbitMqQueueTemplate.convertSendAndReceive(RabbitMqConstant.FANOUT_EXCHANGE_NAME, "", message);
break;
default:
result = rabbitMqQueueTemplate.convertSendAndReceive(RabbitMqConstant.DIRECT_EXCHANGE_NAME, routingKey, message);
}
return result;
}
/**
* 根据队列名动态添加监听队列
* @param queueName 队列名
*/
public void addListener(String queueName) {
messageListenerContainer.addQueueNames(queueName);
messageListenerContainer.setMessageListener(messageListener);
}
/**
* 从监听中 移除指定队列
* @param queueName 队列名
*/
public void removeListener(String queueName) {
messageListenerContainer.removeQueueNames(queueName);
}
-
使用注解@RabbitListener方式
/** * 使用注解监听指定队列 * 需要在项目启动前,就指定需要监听的队列 * * @param message */ @RabbitListener(queues = RabbitMqConstant.QUEUE_NAME_ONE) public void receiveMessage(String message) { log.debug("消费端获取到信息:{}", message); }
-
动态添加监听队列时,需要实现MessageListener接口的onMessage方法。
@Override public void onMessage(Message message) { String mes = new String(message.getBody()); log.debug("监听到消息: {}", mes); }
交换机类型枚举 ExchangeType
/**
* 直连交换机
* 消息中的路由键与绑定的key完全匹配时才会将消息投放到响应的队列,Direct Ex
*/
DIRECT_EXCHANGE("directExchange"),
/**
* 主题交换机
* 消息中的路由键与绑定的Key模糊匹配,可以使用通配符匹配。Binding Key可以
*/
TOPIC_EXCHANGE("topicExchange"),
/**
* 扇形交换机
* 不需要路由键(Routing Key),向所有绑定的队列广播消息。
*/
FANOUT_EXCHANGE("fanoutExchange"),
/**
* 头交换机
* 使用多个属性匹配消息路由,可以看作是Topic Exchange的升级版,性能较差。
*/
HEADERS_EXCHANGE("headersExchange");
private String name;
ExchangeType(String name) {
this.name = name;
}
测试 RabbitMqController
@Autowired
private MessageProducer producer;
@Autowired
private RabbitMqQueueTemplate rabbitMqQueueTemplate;
/**
* 创建MQ队列和交换机,并绑定交换机和队列
*/
@PostMapping("/create")
public R createQueueAndBindToExchange(String exchangeName, String queueName, String routingKey, ExchangeType exchangeType) {
rabbitMqQueueTemplate.createQueueAndBindToExchange(exchangeName, queueName, routingKey, exchangeType);
return R.success("队列、交换机创建,并绑定完成!");
}
@PostMapping("/sendMessage")
public R sendMessage(String message, String routingKey, ExchangeType exchangeType) {
log.debug("ExchangeType: {}", exchangeType);
Object result = producer.sendMessage(message, routingKey, exchangeType);
return R.data(result);
}
/**
* 动态指定需要监听的队列
* @param queueName 队列名
*/
@PostMapping("/addListener")
public R addListener(String queueName) {
producer.addListener(queueName);
return R.success("队列监听成功!");
}
@DeleteMapping("/removeListener")
public R removeListener(String queueName) {
producer.removeListener(queueName);
return R.success("移除监听队列!");
}