RabbitMQ
RabbitMQ安装
用docker安装:
version: "3.1"
services:
rabbitmq:
image: daocloud.io/library/rabbitmq:management
restart: always
container_name: rabbitmq
ports:
- 5672:5672
- 15672:15672
volumes:
- ./data:/var/lib/rabbitmq
RabbitMQ架构
-
Publisher - 生产者:发布消息到RabbitMQ中的Exchange
-
Consumer - 消费者:监听RabbitMQ中的Queue中的消息
-
Exchange - 交换机:和生产者建立连接并接收生产者的消息
-
Queue - 队列:Exchange会将消息分发到指定的Queue,Queue和消费者进行交互
-
Routes - 路由:交换机以什么样的策略将消息发布到Queue!
RabbitMQ通信方式
- Hello World
一个生产者,一个默认的交换机,一个队列,一个消费者 - Work queues
一个生产者,一个默认的交换机,一个队列,两个消费者 - Publish/Subscribe
一个生产者,一个交换机,两个队列,两个消费者 - Routing
一个生产者,一个交换机,两个队列,两个消费者 - Topics
一个生产者,一个交换机,两个队列,两个消费者 - RPC
- Publisher Confirms
SpringBoot与RabbitMQ
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件
spring:
rabbitmq:
host: 目标主机
port: 5672
username: 用户名
password: 密码
virtual-host: 工作空间
声明exchange、queue
@Configuration
public class RabbitMQConfig {
//1. 创建exchange - topic
@Bean
public TopicExchange getTopicExchange(){
return new TopicExchange("交换机名",true,false);
}
//2. 创建queue
@Bean
public Queue getQueue(){
return new Queue("队列名",true,false,false,null);
}
//3. 绑定在一起
@Bean
public Binding getBinding(TopicExchange topicExchange,Queue queue){
return BindingBuilder.bind(queue).to(topicExchange).with("路由规则");
}
}
发布消息到RabbitMQ
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void contextLoads() {
rabbitTemplate.convertAndSend("交换机名","路由规则","发送内容");
}
消费者监听队列
@Component
public class Consumer {
@RabbitListener(queues = "队列名")
public void getMessage(Object message){
System.out.println("接收到消息:" + message);
}
}
手动Ack
-
什么是ACK?
ACK是一个应答机制,是消费者给MQ的应答机制 -
自动ACK
MQ把消息推送给消费者,只要消费者不出现异常就会认为这个消息已经被正常消费了;
MQ把消息推送给消费者,消费者出现异常,MQ会认为信息无法消费,就会把这个消息压到队列头部。 -
手动ACK
MQ把消息推送给消费者,消费者如果成功了就应答ACK,否则就应答NACK -
如何配置手动ACK?
在yml中开启手动ACK
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual
- 开启手动ACK
在监听中添加两个属性
Channel:通道
Message:消息本身属性
Object:MQ中的消息
MQ中消息状态变化
Ready:待分配状态(没有推送给消费者的消息数量)
Ready:待应答(已经推送给消费者的的消息数量,等待消费者应答的数量)
Total:队列中的消息总条数
消息的可靠传输
①、提供者发送消息到交换机-----》confirm机制确认
②、从交换机路由到队列-------》return机制确认
③、消息到队列后MQ宕机了-----》MQ持久化
1)在MQ持久化的过程中宕机了:镜像机制
④、消费者确认手动消息-----》手动ACK
过期时间
过期时间TTL表示可以对消息设置预期的时间,在这个时间内都可以被消费者接收获取;超过时间之后消息将会自动被删除,MQ可以对消息和队列设置TTL。目前有两种方法:
1、通过队列属性设置,队列中的所有消息都有相同的过期时间;
2、通过对消息进行单独设置,每条消息TTL可以不同。
如果同时使用两种方法,则以两者之间较小的数值为准。消息在队列的生存时间一旦过了TTL值,被称为dead message被投递到死信队列,消费者无法再收到该消息。
@Bean
public Queue getQueue(){
//定义队列过期时间
Map<String,Object> args = new HashMap<>();
args.put("x-message-ttl", 5000);
return new Queue("ttl.topic.queue", true, false, false, args);
}
死信队列
DLX,全称Dead-Letter-Exchange,称为死信交换机,当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换机中,这个交换机就是DLX,绑定DLX的队列就称之为死信队列。消息变成死信,可能是由于以下的原因:
1)消息被拒绝
2)消息过期
3)队列达到最大长度
DLX也是一个正常的交换机,和一般队列没区别,它能在任何的队列上被指定,实际上就是设置某一个队列的属性。当这个队列中存在死信时,MQ就会自动地将这个消息重新发布到设置DLX上去,进而被路由到另一个队列,即死信队列。
想要使用死信队列,只需要在定义队列的时候设置队列参数 x-dead-letter-exchange 指定交换机。
@Bean
public Queue getQueue(){
//定义队列过期时间
Map<String,Object> args = new HashMap<>();
args.put("x-message-ttl", 5000);
//与死信队列产生关系
args.put("x-dead-letter-exchange", "dead.exchange");
args.put("x-dead-letter-routing-key", "dead");
return new Queue("ttl.topic.queue", true, false, false, args);
}```