简介
Advanced Message Queuing Protocol(简称AMQP),是应用于在应用程序或之间传递业务信息的开发标准。该协议与语言和平台无关,更符合微服务中的独立性要求
Spring AMQP是基于AMQP协议定义的一套API规范,提供了模板来发送和接收消息。包含两部分,其中spring-rabbit是底层的默认实现
基础功能
RabbitMQ安装
在使用之前需要安装RabbitMQ,安装教程
官方安装包下载有点慢,这里提供了阿里云盘分享的安装包:rabbitmq-server-3.11.8.exe
注意:当执行命令报rabbitmq erlang_home not set correctly.错误时,rabbitmq_server-3.11.8\sbin目录中找到rabbitmq-server.bat进行编辑,并找到如下代码
if not exist "!ERLANG_HOME!\bin\erl.exe" (
echo.
echo ******************************
echo ERLANG_HOME not set correctly.
echo ******************************
echo.
echo Please either set ERLANG_HOME to point to your Erlang installation or place the
echo RabbitMQ server distribution in the Erlang lib folder.
echo.
exit /B 1
)
请检查"!ERLANG_HOME!\bin\erl.exe"
与环境变量Path是否配置正确,安装好后浏览器访问localhost:15672
即可
引入Maven依赖
<!--amqp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
发送端代码实现
application.yml配置
spring:
rabbitmq:
host: localhost # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机
username: guest # 用户名
password: guest # 密码
注意:这里用的是
5672
端口,同时我使用的是系统默认的账号密码对应网页端如下
Java代码如下
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void contextLoads() {
String queueName = "simple.queue";
String message = "发送消息";
rabbitTemplate.convertAndSend(queueName, message);
}
其中queueName如下
执行该代码即可发送消息
接收端代码
@Component
public class SpringRabbitListener {
// queues里使用的是队列的名称
@RabbitListener(queues = "simple.queue")
public void listenerSimpleQueue(String message) {
System.out.println("接收到的消息 : " + message);
}
}
注意:SpringAMQP一旦消费了消息就会销毁
WorkQueue
Work Queue即工作队列,可以提高消息的处理速度,避免消息堆积。由于一个消息生产者,一个消息消费者,一个消息队列。由于消息生产者比消息消费者生产的速度大于消费的速度,就会导致消息队列堆积,当消息队列堆积满后就导致消息丢失。
绑定多个监听者
@RabbitListener(queues = "simple.queue")
public void listenerWorkQueue1(String message) throws InterruptedException {
System.out.println("work1接收到的消息 : " + message);
Thread.sleep(20);
}
@RabbitListener(queues = "simple.queue")
public void listenerWorkQueue2(String message) throws InterruptedException {
System.err.println("work2接收到的消息 : " + message);
Thread.sleep(200);
}
发送多条消息
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void sendMessage() {
String queueName = "simple.queue";
String message = "发送消息 -- ";
for (int i = 0; i < 50; i++) {
rabbitTemplate.convertAndSend(queueName, message + i);
}
}
由于RabbitTemplate默认会采用轮询策略来分发给不同的消费者,所以会导致work1和work2都获取到了25条消息。但现在由于消费者的消费能力不一致,所以希望消费能力强的消费者多消费来避免消息发送出现的拥堵情况
在application.yml文件配置如下来达到要求
spring:
rabbitmq:
host: localhost # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机
username: guest # 用户名
password: guest # 密码
listener:
simple:
prefetch: 1 # 每次只能获取一条消息,处理完才能获取下一条消息
发布订阅模型
发布订阅模式是允许将同一消息发送给多个消费者。实现方式是加入了exchange(交换机)
常见exchange类型
1、Fanout广播
2、Direct路由
3、Topic话题
Fanout Exchange
Fanout Exchange会将接收到的消息路由到每一个跟其绑定的queue
fanout Exchange发送消息图解:
其中fount Exchange的名称为itcast.fanout
代码实现
1、配置类
@Configuration
public class FanoutConfig {
/**
* 说明交换机
* @return
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("itcase.fanout");
}
/**
* 声明队列
* @return
*/
@Bean
public Queue fanoutQueue1() {
return new Queue("fanout.queue1");
}
@Bean
public Queue fanoutQueue2() {
return new Queue("fanout.queue2");
}
/**
* 绑定交换机和队列
* @param fanoutQueue1
* @param fanoutExchange
* @return
*/
@Bean
public Binding bindingFanoutExchange1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
}
@Bean
public Binding bindingFanoutExchange2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}
}
2、监听者
@RabbitListener(queues = "fanout.queue1")
public void listenerFanoutQueue1(String message) {
System.out.println("fanout queue1接收到的消息 : " + message);
}
@RabbitListener(queues = "fanout.queue2")
public void listenerFanoutQueue2(String message) {
System.out.println("fanout queue2接收到的消息 : " + message);
}
3、发送测试
@Test
void listenFanoutExchange() {
String queueName = "itcase.fanout";
String message = "dear all hello queue";
rabbitTemplate.convertAndSend(queueName, "", message);
System.out.println("发送消息结束");
}
Direct Exchange
Direct Exchange会根据规则路由发送到指定的Queue。
每一个Queue都与Exchange设置BindingKey,发布者发送消息时,指定消息的RoutingKey,Exchange将消息路由到BindingKey与消息的RoutingKey一致的队列
代码实现
1、监听者
@Component
public class SpringRabbitListener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"), // 队列名称
exchange = @Exchange(name = "itcase.direct", type = ExchangeTypes.DIRECT), // 交换机的名称以及类型
key = {"red", "blue"}
))
public void listenDirectQueue1(String message) {
System.out.println("queue1 接收到的 " + message);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"), // 队列名称
exchange = @Exchange(name = "itcase.direct", type = ExchangeTypes.DIRECT), // 交换机的名称以及类型
key = {"red", "yellow"}
))
public void listenDirectQueue2(String message) {
System.out.println("queue2 接收到的 " + message);
}
}
2、发送测试
@Test
void sendDirectExchange() {
String queueName = "itcase.direct";
String message = "dear all hello direct";
rabbitTemplate.convertAndSend(queueName, "red", message);
System.out.println("发送消息结束");
}
Topic Exchange
Topic Exchange与Direct Exchange类似,区别在于routingKey必须是多个单词的类别,并以
.
分割
Queue与Exchange指定BindingKey时可以使用通配符
#
代指0个或多个单词
*
代指一个单词
代码实现
1、监听者
@Component
public class SpringRabbitListener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue1"),
exchange = @Exchange(name = "itcase.topic", type = ExchangeTypes.TOPIC),
key = "china.#"
))
public void ListenTopicQueue1(String message) {
System.out.println("queue1 接收到的 " + message);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue2"),
exchange = @Exchange(name = "itcase.topic", type = ExchangeTypes.TOPIC),
key = "#.news"
))
public void ListenTopicQueue2(String message) {
System.out.println("queue2 接收到的 " + message);
}
}
2、发送
@Test
void sendTopicExchange() {
String queueName = "itcase.topic";
String message = "dear all hello topic china weather ";
rabbitTemplate.convertAndSend(queueName, "china.weather", message);
System.out.println("发送消息结束");
}
消息转化器
SpringAMQP支持发送对象,由于MQ底层只能发送字节,所以需要序列化。而Spring本身自带的序列化是比较长占内存空间传输速度慢且不易读
引入Maven依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.14.2</version>
</dependency>
注意:如果你使用的
spring boot
较高的版本,spring boot
内部已经有了jackson-dataformat-xml
依赖了,无需导入该依赖否则启动不了项目
1、配置类
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public Queue messageConverter() {
return new Queue("converter.queue");
}
注意引入的是
org.springframework.amqp.support.converter.MessageConverter;
的包
2、监听者
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "converter.queue")
public void listenMessageConverter(Map<String, String> message) {
System.out.println("map value is " + message);
}
}
3、发送
@Test
void sendMessageConverter() {
String routingKey = "converter.queue";
Map<String, String> map = new HashMap<>();
map.put("hello", "world");
rabbitTemplate.convertAndSend(routingKey, map);
System.out.println("发送消息结束");
}