RabbitMQ 回顾梳理
记录小白(也就是本人😸)学习RabbitMQ的第一视角。
0、简介
0.0 介绍RabbitMQ
RabbitMQ是一个流行开源信息中间件,实现了高级信息队列协议(AMQP),为分布式应用程序提供了可靠的、异步的消息传播机制。
RabbitMQ核心概念是生产者将信息发布到队列中。消费者从队列中获取信息。
0.1 RabbitMQ优点
- 它使用消息确认机制确保信息能够成功传递。同时提供了多种交换机类型和绑定方式,以支持不同的信息路由场景。(可靠性)
- RabbitMQ 可以满足高负载、高可用性和可扩展性的要求。(灵活性、可扩张性)
- RabbitMQ 提供了丰富的客户端库,包括 Java、Python、Ruby、C# 等,这些库可以方便地集成到各种编程语言和框架中,以实现高效的消息传递。因此,RabbitMQ 在大规模分布式系统中得到了广泛的应用。
开启 web 管理插件rabbitmq-plugins enable rabbitmq_management
1、SpringBoot整合
依赖pom.xml
<!--rabbitmq-需要的 AMQP 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置application.yml
spring:
rabbitmq:
# RabbitMQ 的 ip
host: 192.168.153.129
username: guest
password: guest
# 虚拟主机
virtual-host: /
# 端口
port: 5672
listener:
simple:
# 消费者最小连接数
concurrency: 10
# 消费者最大数量
max-concurrency: 10
# 限制消费者,每次只能处理一条信息,处理完才能处理下一条消息
prefetch: 1
# 启动时,是否默认启动容器,默认是true
auto-startup: true
# 被拒绝后是否重新进入队列
default-requeue-rejected: true
template:
retry:
# 启用重试机制,默认false
enabled: true
# 设置初始化的重试时间间隔
initial-interval: 1000ms
# 重试最大次数,默认3
max-attempts: 3
# 重试最大时间间隔,默认10s
max-interval: 10000ms
# 重试时间间隔的层数
multiplier: 1
配置类 RabbitMQConfig
package com.jyu.seckill.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author xiaoyu
* @version 1.0
*/
@Configuration
public class RabbitMQConfig {
// 定义队列名
private static final String QUEUE = "queue";
@Bean
public Queue queue() {
//创建队列
return new Queue(QUEUE, true); // true:队列持持久化
}
}
发送消息
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author xiaoyu
* @version 1.0
*/
@Service
@Slf4j
public class MQSender {
// 装配RabbitTemplate
@Resource
private RabbitTemplate rabbitTemplate;
public void send(Object msg) {
log.info("发送信息-->{}",msg);
rabbitTemplate.convertAndSend("queue",msg);
}
}
接受信息
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author xiaoyu
* @version 1.0
*/
@Service
@Slf4j
public class MQReceiver {
//接受消息
@RabbitListener(queues = "queue")
private void receive(Object obj) {
log.info("接受信息-->{}",obj);
}
}
2、Rabbit MQ的使用模式
2.1 Fanout
fanout 就是广播模式, 就是把交换机(Exchange)里的消息发送给所有绑定该交换机的队列,忽略routingKey(也就是路由)。
2.1.1 案例
1首先创建交换机 --> 2创建两个队列 --> 3队列绑定到交换机 -->4 发送信息测试
结果两个队列都能收到信息
步骤1、步骤2 和步骤3
@Configuration
public class RabbitMQConfig {
// fanout 模式
private static final String QUEUE1 = "queue_fanout1";
private static final String QUEUE2 = "queue_fanout2";
private static final String EXCHANGE = "fanout_exchange";
@Bean
public Queue queue_fanout1() {
return new Queue(QUEUE1);
}
@Bean
public Queue queue_fanout2() {
return new Queue(QUEUE2);
}
// 创建fanout交换机
@Bean
public FanoutExchange exchange() {
return new FanoutExchange(EXCHANGE);
}
/**
* 将QUEUE1绑定到交换机EXCHANGE
*/
@Bean
public Binding binding01() {
return BindingBuilder.bind(queue_fanout1()).to(exchange());
}
/**
* 将QUEUE1绑定到交换机EXCHANGE
*/
@Bean
public Binding binding02() {
return BindingBuilder.bind(queue_fanout2()).to(exchange());
}
}
发送信息
@Service
@Slf4j
public class MQSender {
// 装配RabbitTemplate
@Resource
private RabbitTemplate rabbitTemplate;
//fanout 向交换机发送信息
public void sendToExchange(Object msg) {
log.info("fanout模式: 发送信息-->{}",msg);
//忽略routingKey路由
rabbitTemplate.convertAndSend("fanout_exchange","",msg);
}
}
接收消息
@Service
@Slf4j
public class MQReceiver {
@RabbitListener(queues = "queue_fanout1")
private void receive1(Object obj) {
log.info("queue_fanout1 : 接受信息-->{}",obj);
}
@RabbitListener(queues = "queue_fanout2")
private void receive2(Object obj) {
log.info("queue_fanout2 : 接受信息-->{}",obj);
}
}
2.2 Direct
direct 就是路由模式, 路由模式是在使用交换机的同时,生产者指定路由发送数据,消费者绑定路由接受数据。
路由模式与广播模式不同的是,广播模式只要是绑定了交换机的队列都会收到生产者向交换机推送过来的数据。而路由模式下加了一个路由设置,生产者向交换机发送数据时,会声明发送给交换机下的哪个路由,并且只有当消费者的队列绑定了交换机并且声明了路由,才会收到数据。
2.2.1 案例
@Configuration
public class RabbitMQConfig {
//Direct
private static final String QUEUE_DIRECT1 = "queue_direct01";
private static final String QUEUE_DIRECT2 = "queue_direct02";
private static final String EXCHANGE_DIRECT = "direct_exchange";
private static final String routingkey01 = "queue.red";
private static final String routingkey02 = "queue.green";
@Bean
public Queue queueDirect01() {
return new Queue(QUEUE_DIRECT1);
}
@Bean
public Queue queueDirect02() {
return new Queue(QUEUE_DIRECT2);
}
@Bean
public DirectExchange directExchange() {
return new DirectExchange(EXCHANGE_DIRECT);
}
@Bean
public Binding binging01_direct() {
return BindingBuilder.bind(queueDirect01()).to(directExchange()).with(routingkey01);
}
@Bean
public Binding binging02_direct() {
return BindingBuilder.bind(queueDirect02()).to(directExchange()).with(routingkey02);
}
}
2.3 Topic
1、direct 模式会造成路由 RoutingKey 太多, 而实际开发中往往是按照某个规则来进行路由匹配的,RabbitMQ 提供了 Topic 模式/主题模式来适应这种需求。
2、Topic 模式是 direct 模式上的一种扩展/叠加, 扩展/叠加了模糊路由 RoutingKey 的模式, 可以理解为是模糊的路由匹配模式。
*(星号):可以(只能)匹配一个单词。
#(井号):可以匹配多个单词(或者零个)。
2.3.1 案例
@Configuration
public class RabbitMQConfig {
private static final String QUEUE01 = "queue_topic01";
private static final String QUEUE02 = "queue_topic02";
private static final String EXCHANGE = "topicExchange";
private static final String ROUTINGKEY01 = "#.queue.#";
private static final String ROUTINGKEY02 = "*.queue.#";
@Bean
public Queue queueTopic01() {
return new Queue(QUEUE01);
}
@Bean
public Queue queueTopic02() {
return new Queue(QUEUE02);
}
@Bean
public TopicExchange topicExchange() {
return new TopicExchange(EXCHANGE);
}
@Bean
public Binding bindingTopic01() {
return BindingBuilder.bind(queueTopic01()).to(topicExchange()).with(ROUTINGKEY01);
}
@Bean
public Binding bindingTopic02() {
return BindingBuilder.bind(queueTopic02()).to(topicExchange()).with(ROUTINGKEY02);
}
}
2.4 Headers
- headers 模式/headers 头路由模式 使用比较少。
- headers 交换机是一种比较复杂且少见的交换机,不同于 direct 和 topic,它不关心路由 key 是否匹配,而只关心 header 中的key-value 对是否匹配(这里的匹配为精确匹配,包含键和值都必须匹配), 有点类似于 http 中的请求头。
- headers 头路由模型中,消息是根据 prop 即请求头中 key-value 来匹配的。
- 绑定的队列(也可以理解成消费方) 指定的 headers 中必须包含一个"x-match"的键。
- 键"x-match"的值有 2 个:all 和 any。
all:表示绑定的队列/消费方 指定的所有 key-value 都必须在消息 header 中出现并匹配。
any:表示绑定的队列/消费方 指定的 key-value 至少有一个在消息 header 中出现并匹配即可。
2.4.1案例
RabbitMQConfig
@Configuration
public class RabbitMQConfig {
private static final String QUEUE1 = "queue_headers1";
private static final String QUEUE2 = "queue_headers2";
private static final String HEADERS_EXCHANGE = "headersExchange";
@Bean
public Queue queueHeaders1() {
return new Queue(QUEUE1);
}
@Bean
public Queue queueHeaders2() {
return new Queue(QUEUE2);
}
@Bean
public HeadersExchange headersExchange() {
return new HeadersExchange(HEADERS_EXCHANGE);
}
@Bean
public Binding bindingHeaders1() {
Map<String,Object> mp = new HashMap<>();
mp.put("name","jack");
mp.put("color","blue");
return BindingBuilder.bind(queueHeaders1()).to(headersExchange()).whereAll(mp).match();
}
@Bean
public Binding bindingHeaders2() {
Map<String, Object> mp = new HashMap<>();
mp.put("name", "tom");
mp.put("color", "blue");
return BindingBuilder.bind(queueHeaders2()).to(headersExchange()).whereAny(mp).match();
}
}
MQSender
@Service
@Slf4j
public class MQSender {
// 装配RabbitTemplate
@Resource
private RabbitTemplate rabbitTemplate;
public void sendByHeaders1(String msg) {
log.info("headers1模式: 发送信息-->{}",msg);
// 创建消息属性
MessageProperties messageProperties = new MessageProperties();
messageProperties.setHeader("name","jack");
messageProperties.setHeader("color","blue");
Message message = new Message(msg.getBytes(), messageProperties);
rabbitTemplate.convertAndSend("headersExchange","",message);
}
public void sendByHeaders2(String msg) {
log.info("headers2模式: 发送信息-->{}",msg);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setHeader("name","tom");
messageProperties.setHeader("color","blue");
Message message = new Message(msg.getBytes(), messageProperties);
rabbitTemplate.convertAndSend("headersExchange","",message);
}
}
接收信息
@Service
@Slf4j
public class MQReceiver {
@RabbitListener(queues = "queue_headers1")
public void queue_headers01(Message message) {
log.info("queue_headers1 : 接收信息-->{}",new String(message.getBody()));
}
@RabbitListener(queues = "queue_headers2")
public void queue_headers02(Message message) {
log.info("queue_headers2 : 接收信息-->{}",new String(message.getBody()));
}
}