目录
RabbitMQ 是一种可靠且成熟的消息传递和流媒体代理
一、安装
docker启动RabbitMQ容器,
sudo docker run -d --rm \
-e RABBITMQ_DEFAULT_USER=jungle \
-e RABBITMQ_DEFAULT_PASS=123456 \
--name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
rabbitmq:3.13-management
--rm
:表示容器退出时自动删除容器。这意味着当容器停止后,它的文件系统和其他内容将会被删除。如果你想保留容器的数据,可以省略此参数。- RABBITMQ_DEFAULT_USER=用户名
- RABBITMQ_DEFAULT_PASS=密码
- 5672 控制台端口
- 15672 平台访问端口
换成docker-compose.yml内容:
version: '3.8'
services:
rabbitmq:
image: rabbitmq:3.13-management
container_name: rabbitmq
ports:
- "5672:5672" #控制台端口
- "15672:15672" #平台访问端口
ip+端口访问,输入自己设置的用户名密码登录,我的是http://172.30.171.205:15672/
进入:
二、使用Spring AMQP进行消息传递
Spring AMQP提供了一个 "Template",作为发送和接收消息的高级抽象。
在项目中父工程引入依赖:
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
【注意】AMQP依赖,包含RabbitMQ,无需引入以下rabbitmq的依赖,
<!-- Spring AMQP -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>3.1.4</version>
</dependency>
引入可能会报错
JDK版本不对应
consumer thread error, thread abort.(消费者线程错误,线程中止)
消息发送
Spring官方文档: What’s New :: Spring AMQP
https://docs.spring.io/spring-amqp/reference/whats-new.html
添加队列 simple.queue :
在需要做消息发送的服务A中application.yml文件中添加以下配置信息,对RabbitMQ服务主机名、端口、虚拟主机、用户名、密码进行配置
spring: rabbitmq: host: 172.30.171.205 # 主机名 port: 5672 # 端口 virtual-host: / # 虚拟主机 username: jungle # 用户名 password: 123456# 密码
编写测试类,测试消息发送
@SpringBootTest
public class PublisherTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void publisher(){
// 队列名称
String queueName = "simple.queue";
// 消息
String message = "消息发送内容,hello AMQP !";
// 发送消息
rabbitTemplate.convertAndSend(queueName, message);
}
}
接收消息(消费者)
在需要接收消息的服务中,依然需要引依赖和加RabbitMQ的配置信息,新建一个类SpringRabbitListener测试接收消息,启动服务后,可接收simple.queue队列消息
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "simple.queue")
public void listenSimpleQueueMessage(String msg){
System.out.println("Spring 消费者接收到消息:【" + msg + "】");
}
}
当使用多个消费者去处理消息时,配置消费预取限制,根据服务的能力,规定每次能处理的消息数量
spring:
rabbitmq:
listener:
simple:
prefetch: 1 #每次只能获取一条消息,处理完成才能获取下一个消息
消息发送(FannoutExchange)
所有订阅的队列都消费
声明交换机、队列以及消费者中配置队列绑定到交换机的关系(可在rabbitmq平台页面查看):
声明交换机、队列和绑定关系的Bean 时注意方法名不用重复,绑定关系的方法的参数类型和参数名要与之前声明Bean的方法名对应,例如方法FanoutExchange fanoutExchange(){}和方法Queue fanoutQueue1(){} 对应参数 Binding bindQueue1(FanoutExchange fanoutExchange,Queue fanoutQueue1){},参数名和方法名不对应则会使注入对象失败:Could not autowire.(不能自动注入)
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FanoutConfiguration {
//交换机 jungle.fanout
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("jungle.fanout");
}
//队列1 fanout.queue1
@Bean
public Queue fanoutQueue1(){
return new Queue("fanout.queue1");
}
//队列2 fanout.queue2
@Bean
public Queue fanoutQueue2(){
return new Queue("fanout.queue2");
}
//绑定队列1到交换机 fanout.queue1
@Bean
public Binding bindQueue1(FanoutExchange fanoutExchange,Queue fanoutQueue1){
return BindingBuilder
.bind(fanoutQueue1)
.to(fanoutExchange);
}
//绑定队列2到交换机 fanout.queue1
@Bean
public Binding bindQueue2(FanoutExchange fanoutExchange,Queue fanoutQueue2){
return BindingBuilder
.bind(fanoutQueue2)
.to(fanoutExchange);
}
}
向交换机为jungle.fanout的所有队列发送消息
/**
* 向多个消费者发送同一消息
*/
@Test
public void publisherFanoutExchange(){
// 交换机名称
String exchangeName = "jungle.fanout";
// 消息
String message = "消息群送内容,hello,everyone AMQP !";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName,"", message);
}
消息发送(DirectExchange)
含有指定的routingKey的队列消息才会被消费
发送消息,指定routingKey为keyName1
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 向多个消费者发送同一消息,含有routingKey为keyName1
*/
@Test
public void publisherFanoutExchange(){
// 交换机名称
String exchangeName = "jungle.fanout";
// 消息
String message = "消息群送内容,hello,everyone AMQP !";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName,"keyName1", message);
}
以下例子中,监听jungle.direct交换机中的direct.queue1队列,并且routingKey有keyName1、keyName2的消息才会被消费。
@RabbitListener(bindings = @QueueBinding(
value = @Queue("direct.queue1"),
exchange = @Exchange(name = "jungle.direct",type = ExchangeTypes.DIRECT),
key = {"keyName1","keyName2"}
))
public void listenDirectQueue(String msg) {
System.out.println("消费者2 接收到direct消息:【" + msg + "】");
}
消息发送(TopicExchange)
可用使用通配符来指定routingKey的值,routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: china.weacher
通配符规则:
#:匹配一个或多个词
*:只匹配1个词
例如:china.*
//监听jungle.topic.queue1队列,并且routingKey满足通配符china.# 的消息
@RabbitListener(bindings = @QueueBinding(
value = @Queue("topic.queue1"),
exchange = @Exchange(name = "jungle.direct",type = ExchangeTypes.TOPIC),
key = {"china.#"}
))
public void listenTopicQueue(String msg) {
System.out.println("消费者2 接收到topic消息:【" + msg + "】");
}
【注意】在rabbitmq页面创建对应的交换机、队列或在配置类中声明对应的交换机或队列
消息转换器:
默认使用JDK序列化方式,消息体积大,可读性小
在启动类中添加或者自己新建一个配置类添加:
//配置消息转换器类型,JDK序列化方式并不合适。希望消息体的体积更小、可读性更高,因此可以使用JSON方式来做序列化和反序列化
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
发送消息:
@Test
public void publisher(){
// 队列名称
String queueName = "simple.queue";
// 消息
Student student =new Student();
student.setName("Jungle");
student.setAge(25);
// 发送消息
rabbitTemplate.convertAndSend(queueName, student);
}
接收消息: