在SpringBoot中使用RabbitMQ
RabbitMQ简介
RabbitMQ在CentOS上安装
配置文件
application.properties
spring.rabbitmq.host=119.29.213.48
spring.rabbitmq.port=5672 # 注意这里
spring.rabbitmq.username=xxx
spring.rabbitmq.password=xxx
spring.rabbitmq.virtual-host=/
pom.xml
我是通过maven来构建项目的。因此在一个SpringBoot项目的基础上还需要添加一个依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
实践
概述
RabbitMQ我学习到的有四种模式:
分别是 Direct
,Topic
,Fanout
,Headers
模式。
Direct
模式就是直接用队列的名字来进行绑定,实现点对点的消息传输。
Topic
模式是根据 Config
中设置的 RoutineKey
还有发送消息时候的 topic
来判断是否会传输。
Fanout
模式类似于广播,不用设置路由,只要发送消息设置了对应的 Exchange
就可以对该 Exchange
中的接收者进行广播。
Headers
模式比较不同于其他三种模式。Headers
是一个键值对,可以定义成 Hashtable。发送者在发送的时候定义一些键值对,接收者也可以再绑定时候传入一些键值对,两者匹配的话,则对应的队列就可以收到消息。匹配有两种方式all和any。
Demo
下面是一些实践的代码。
MQConfig.java
package com.psd.rabbitmq.rabbitmq;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class MQConfig {
// 四种模式:Direct,Topic,Fanout,Header
public static final String DIRECT_QUEUE = "direct_queue";
public static final String TOPIC_QUEUE_1 = "topic_queue_1";
public static final String TOPIC_QUEUE_2 = "topic_queue_2";
public static final String TOPIC_EXCHANGE = "topic_exchange";
public static final String ROUTINE_KEY_1 = "topic.key1";
// 可以使用通配符
public static final String ROUTINE_KEY_2 = "topic.*";
public static final String FANOUT_QUEUE_1 = "fanout_queue_1";
public static final String FANOUT_QUEUE_2 = "fanout_queue_2";
public static final String FANOUT_EXCHANGE = "fanout_exchange";
public static final String HEADERS_QUEUE = "headers_queue";
public static final String HEADERS_EXCHANGE = "headers_exchange";
// Direct模式
@Bean
public Queue directQueue() {
return new Queue(DIRECT_QUEUE, true);
}
// Topic模式:根据RoutineKey去绑定接收的消息
@Bean
public Queue topicQueue1() {
return new Queue(TOPIC_QUEUE_1, true);
}
@Bean
public Queue topicQueue2() {
return new Queue(TOPIC_QUEUE_2, true);
}
@Bean
public TopicExchange topicExchange() {
return new TopicExchange(TOPIC_EXCHANGE);
}
@Bean
public Binding topicBinding1() {
return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with(ROUTINE_KEY_1);
}
@Bean
public Binding topicBinding2() {
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with(ROUTINE_KEY_2);
}
// Fanout模式:广播
@Bean
public Queue fanoutQueue1() {
return new Queue(FANOUT_QUEUE_1, true);
}
@Bean
public Queue fanoutQueue2() {
return new Queue(FANOUT_QUEUE_2, true);
}
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange(FANOUT_EXCHANGE);
}
@Bean
public Binding fanoutBinding1() {
return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
}
@Bean
public Binding fanoutBinding2() {
return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
}
// Headers模式:只有检验头部的KV是一致的才会接收到消息
@Bean
public HeadersExchange headersExchage(){
return new HeadersExchange(HEADERS_EXCHANGE);
}
@Bean
public Queue headerQueue() {
return new Queue(HEADERS_QUEUE, true);
}
@Bean
public Binding headerBinding() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("h1", "v1");
map.put("h2", "v2");
return BindingBuilder.bind(headerQueue()).to(headersExchage()).whereAll(map).match();
}
}
MQSender.java
package com.psd.rabbitmq.rabbitmq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MQSender {
private static Logger logger = LoggerFactory.getLogger(MQSender.class);
@Autowired
AmqpTemplate amqpTemplate;
public void sendDirect() {
String msg = "DirectMsg";
logger.info("send msg : " + msg);
amqpTemplate.convertAndSend(MQConfig.DIRECT_QUEUE, msg);
}
public void sendTopic() {
String msg = "TopicMsg";
logger.info("send msg : " + msg);
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key1", msg + "_1");
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key2", msg + "_2");
}
public void sendFanout() {
String msg = "FanoutMsg";
logger.info("send msg : " + msg);
amqpTemplate.convertAndSend(MQConfig.FANOUT_EXCHANGE, "", msg);
}
public void sendHeaders() {
String msg = "HeadersMsg";
logger.info("send msg : " + msg);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setHeader("h1", "v1");
messageProperties.setHeader("h2", "v2");
Message message = new Message(msg.getBytes(), messageProperties);
amqpTemplate.convertAndSend(MQConfig.HEADERS_EXCHANGE, "", message);
}
}
MQReceiver.java
package com.psd.rabbitmq.rabbitmq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class MQReceiver {
private static Logger log = LoggerFactory.getLogger(MQReceiver.class);
@RabbitListener(queues = MQConfig.DIRECT_QUEUE)
public void receiveDirect(String msg) {
log.info("Direct Receive : " + msg);
}
@RabbitListener(queues = MQConfig.TOPIC_QUEUE_1)
public void receiveTopic1(String msg) {
log.info("Topic1 Receive : " + msg);
}
@RabbitListener(queues = MQConfig.TOPIC_QUEUE_2)
public void receiveTopic2(String msg) {
log.info("Topic2 Receive : " + msg);
}
@RabbitListener(queues = MQConfig.FANOUT_QUEUE_1)
public void receiveFanout1(String msg) {
log.info("Fanout1 Receive : " + msg);
}
@RabbitListener(queues = MQConfig.FANOUT_QUEUE_2)
public void receiveFanout2(String msg) {
log.info("Fanout2 Receive : " + msg);
}
@RabbitListener(queues = MQConfig.HEADERS_QUEUE)
public void receiveHeaders(byte[] msg) {
log.info("Headers Receive : " + new String(msg));
}
}
RabbitmqApplicationTests.java
package com.psd.rabbitmq;
import com.psd.rabbitmq.rabbitmq.MQSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqApplicationTests {
@Autowired
MQSender mqSender;
@Test
public void contextLoads() {
mqSender.sendDirect();
mqSender.sendTopic();
mqSender.sendFanout();
mqSender.sendHeaders();
}
}
执行结果:
遇到的BUG
启动异常
org.springframework.amqp.AmqpTimeoutException: java.util.concurrent.TimeoutException
at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:74) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:476) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:614) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.createConnection(ConnectionFactoryUtils.java:240) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:1797) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:1771) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:1752) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.core.RabbitAdmin.getQueueProperties(RabbitAdmin.java:338) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.redeclareElementsIfNecessary(AbstractMessageListenerContainer.java:1604) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:963) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at java.base/java.lang.Thread.run(Thread.java:844) [na:na]
Caused by: java.util.concurrent.TimeoutException: null
at com.rabbitmq.utility.BlockingCell.get(BlockingCell.java:77) ~[amqp-client-5.1.2.jar:5.1.2]
at com.rabbitmq.utility.BlockingCell.uninterruptibleGet(BlockingCell.java:120) ~[amqp-client-5.1.2.jar:5.1.2]
at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:36) ~[amqp-client-5.1.2.jar:5.1.2]
at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:494) ~[amqp-client-5.1.2.jar:5.1.2]
at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:306) ~[amqp-client-5.1.2.jar:5.1.2]
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:957) ~[amqp-client-5.1.2.jar:5.1.2]
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:907) ~[amqp-client-5.1.2.jar:5.1.2]
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:847) ~[amqp-client-5.1.2.jar:5.1.2]
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:449) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
... 9 common frames omitted
这个BUG我是通过修改 application.properties
中的 spring.rabbitmq.port
修复好的,这里我一开始使用了http的端口 15672
,发生了上述异常。后面改成了 5672
可以成功连接。
映射2个端口:15672是Web管理界面的端口;5672是MQ访问的端口。
以下是官方解释:
5672, 5671: used by AMQP 0-9-1 and 1.0 clients without and with TLS
15672: HTTP API clients and rabbitmqadmin (only if the management plugin is enabled)
无法自动创建队列
无法创建队列,或者找不到声明的队列。
@Bean
public Queue queue() {
return new Queue(queue_name, true);
}
这里是忘了注入队列的Bean了。