rabbitMQ
1、直接使用docker
拉取镜像
docker pull rabbitmq:3.8
2、启动容器
docker run \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=123456 \
-v mq-plugins:/plugins \
--name rabbit01 \
--hostname rabbit01 --restart=always \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3.8
3、关于端口的介绍
4、进入到rabbit01
容器中
docker exec -it rabbit01 /bin/bash
5、开启可视化界面操作
rabbitmq-plugins enable rabbitmq_management
6、客户端直接访问xx:15672
7、或者直接用别人搞好的镜像
docker run \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=123456 \
-v mq-plugins:/plugins \
--name rabbit02 \
--hostname rabbit02 --restart=always \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3.8-management
spring-boot
中整合1、引入依赖包
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
2、在配置文件中引入配置
server.port=8000
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/
1、简单模式就是一个生产者一个消费者
2、生产者代码,运行下面的代码,查看可视化界面,并不存在消息,原因是因为需要手动创建simple_queue
这个队列
@SpringBootTest(classes = ProducerApplication.class)
public class ProducerTest01 {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void test01() {
/**
* 第一个参数:表示队列的名称
* 第二个参数:表示要发送的数据
*/
rabbitTemplate.convertAndSend("simple_queue", "hello world");
}
}
3、运行后查看可视化界面
4、定义一个消费者来消费消息
package com.example.listener01;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class ConsumerListener01 {
@RabbitListener(queues = "simple_queue")
public void listener01(Message message) {
String msg = new String(message.getBody());
System.out.println("接收到的消息:" + msg);
}
}
work
工作模式1、简单的来理解,就是在上面简单模式下增加几个消费者,如同搬砖一样的,一个搬运工搬不过来,多叫几个人来干活的性质,避免消息堆积
2、同样的要先手动创建队列,在生产者端循环发送数据
@Test
public void test02() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("work_queue", "hello world");
}
}
3、定义2个消费者来一起消费消息
@Component
public class ConsumerListener01 {
@RabbitListener(queues = "work_queue")
public void listener01(Message message) {
String msg = new String(message.getBody());
System.out.println("消费者1接收到的消息:" + msg);
}
}
4、先运行消费者,然后运行生产者
Fanout
,广播模式,将消息全部交给所有与之绑定的队列,这里的router key
为空字符串Direct
,将消息指定到对应的routing key
上Topic
,通配符模式,这里的routing key
根据规则匹配
*
表示一个单词#
表示多个单词Fanout
模式1、使用配置文件的方式创建交换机和队列,并且将他们绑定在一起
package com.example.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitFanoutExchangeConfiguration {
// 交换机
@Bean
public Exchange fanoutExchange() {
return ExchangeBuilder.fanoutExchange("fanout_exchange").durable(true).build();
}
// 创建一个队列
@Bean
public Queue fanoutQueue1() {
return QueueBuilder.durable("fanout_queue1").build();
}
// 创建一个队列
@Bean
public Queue fanoutQueue2() {
return QueueBuilder.durable("fanout_queue2").build();
}
// 队列和交换机绑定
@Bean
public Binding fanoutExchangeQueue01() {
// with表示路由key
return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange()).with("").noargs();
}
@Bean
public Binding fanoutExchangeQueue02() {
// with表示路由key
return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange()).with("").noargs();
}
}
2、生产者发送消息
@Test
public void test03() {
rabbitTemplate.convertAndSend("fanout_exchange", "", "hello world");
}
3、查看可视化界面,会自动创建一个交换机和两个路由key
4、定义消费者
@Component
public class ConsumerListener01 {
@RabbitListener(queues = "fanout_queue1")
public void listener01(Message message) {
String msg = new String(message.getBody());
System.out.println("消费者1接收到的消息:" + msg);
}
}
5、这时候会发现,几个消费者都会同时输出
Direct
模式Topic
模式key
的时候使用*
或者#
来表示1、正常创建交换机和队列的方式有三种方式
java api
方式一个一个创建需要先创建交换机、队列,并且让队列和交换机绑定在一起2、使用注解的方式直接来创建交换机和队列
package com.example.listener04;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class ConsumerListener01 {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "queue_01", durable = "true"),
exchange = @Exchange(value = "direct_rabbit_exchange", type = ExchangeTypes.DIRECT),
key = {"info", "error"}
))
public void listener01(Message message) {
String msg = new String(message.getBody());
System.out.println("消费者1接收到的消息:" + msg);
}
}
3、运行后查看rabbitmq
可视化界面
4、定义发送消息的方法
public void test04() {
rabbitTemplate.convertAndSend("direct_rabbit_exchange","error","hello world");
}
1、开启生产者确认机制
server.port=9000
spring.rabbitmq.host=123.56.103.229
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/
# 开启生产者确认机制
spring.rabbitmq.publisher-confirm-type=correlated
2、重写RabbitTemplate
,只要我们在容器中有一个RabbitTemplate
,那么spring boot
就不会用对RabbitTemplate
自动化配置
package com.example.config;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfiguration {
/**
* ConnectionFactory 由于Spring Boot根据连接的配置信息实现自动化配置,在spring容器中是直接存在ConnectionFactory对象
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback(){
@Override
public void confirm(CorrelationData correlationData, boolean ack, String s) {
if (ack) {
System.out.println("消息正常投递到交换机中");
} else {
System.out.println("消息投递到交换机失败:"+s);
}
}
});
return rabbitTemplate;
}
}
3、发送消息的时候故意写错交换机的名字
@Test
public void test04() throws InterruptedException {
rabbitTemplate.convertAndSend("direct_rabbit_exchange_xx","error","hello world");
Thread.sleep(2000);
}
4、处理消息投送失败的方式
5、在发送消息的时候传递当前唯一的识别id
,这里使用uuid
@Test
public void test04() throws InterruptedException {
String msgUuid = UUID.randomUUID().toString().replace("-", "");
CorrelationData correlationData = new CorrelationData(msgUuid);
rabbitTemplate.convertAndSend("direct_rabbit_exchange", "error", "hello world", correlationData);
Thread.sleep(2000);
}
6、获取当前消息的唯一识别
@Configuration
public class RabbitmqConfiguration {
/**
* ConnectionFactory 由于Spring Boot根据连接的配置信息实现自动化配置,在spring容器中是直接存在ConnectionFactory对象
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback(){
@Override
public void confirm(CorrelationData correlationData, boolean ack, String s) {
if (ack) {
System.out.println("消息正常投递到交换机中");
} else {
String mesId = correlationData.getId();
System.out.println(mesId);
System.out.println("消息投递到交换机失败:"+s);
}
}
});
return rabbitTemplate;
}
}
1、当队列名称写错了,或者不存在的时候会出现这个情况
2、开启生产者回调机制
server.port=9000
spring.rabbitmq.host=123.56.103.229
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/
# 开启生产者确认机制
spring.rabbitmq.publisher-confirm-type=correlated
# 开启生产者回调机制
spring.rabbitmq.publisher-returns=true
3、绑定回退函数
package com.example.config;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfiguration {
/**
* ConnectionFactory 由于Spring Boot根据连接的配置信息实现自动化配置,在spring容器中是直接存在ConnectionFactory对象
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
...
// 绑定回退机制的回调函数
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println(returnedMessage.getMessage());
System.out.println(returnedMessage.getReplyCode());
System.out.println(returnedMessage.getReplyText());
System.out.println(returnedMessage.getExchange());
System.out.println(returnedMessage.getRoutingKey());
}
});
return rabbitTemplate;
}
}
1、rabbitmq
默认是在内存中存储,当服务宕机后数据直接会丢失,消息在spring boot
中持久化是因为框架帮你处理了,修改消息是否持久化可以参考下面
@Test
public void test04() throws InterruptedException {
String msgUuid = UUID.randomUUID().toString().replace("-", "");
CorrelationData correlationData = new CorrelationData(msgUuid);
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
MessageProperties messageProperties = message.getMessageProperties(); // 获取到消息属性对象
messageProperties.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT); // 设置消息不缓存
return message;
}
};
rabbitTemplate.convertAndSend("direct_rabbit_exchange", "error", "hello world",messagePostProcessor, correlationData);
Thread.sleep(2000);
}
1、在spring boot
中消费者应答模式主要有以下几种
none
自动应答,消费者获取到消息以后直接给rabbitmq
返回ack
auto
(默认模式),由spring boot
框架根据业务执行特点决定给rabbitmq
是ack
还是uack
,业务执行正常完成后返回ack
,业务执行中出现异常的时候返回uack
manual
手动应答模式,由程序员自己根据业务执行特点给rabbitmq
返回对应的ack
还是uack
2、配置应答模式
server.port=8000
spring.rabbitmq.host=123.56.103.229
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/
# 配置应答模式
spring.rabbitmq.listener.simple.acknowledge-mode=auto
1、在消费者端添加配置
server.port=8000
spring.rabbitmq.host=123.56.103.229
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/
spring.rabbitmq.listener.simple.acknowledge-mode=auto
# 每次处理10个
spring.rabbitmq.listener.simple.prefetch=10
1、在下面几种情况下会产生死信队列
rabbitmq
返回uack
2、死信队列的架构图
3、创建死信队列
package com.example.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitDlxExchangeConfiguration {
// 创建一个死信交换机
@Bean
public Exchange dlxExchange() {
return ExchangeBuilder.fanoutExchange("dlx_exchange").durable(true).build();
}
// 创建一个死信队列
@Bean
public Queue dlxQueue() {
return QueueBuilder.durable("dlx_queue").maxLength(10).build();
}
// 死信交换机和死信队列绑定
@Bean
public Binding dlxQueueBinding() {
return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("dead").noargs();
}
// 创建一个正常的交换机
@Bean
public Exchange orderExchange() {
return ExchangeBuilder.directExchange("order_exchange").durable(true).build();
}
// 创建一个正常队列
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order_queue").
maxLength(10).
deadLetterExchange("dlx_exchange"). // 死信队列的交换机
deadLetterRoutingKey("dead"). // 死信队列的routingKey
build();
}
// 正常交换机和正常队列绑定
@Bean
public Binding orderQueueBinding() {
return BindingBuilder.bind(orderQueue()).to(orderExchange()).with("info").noargs();
}
}
4、发送消息
@Test
public void test05() throws InterruptedException {
for (int i = 0; i < 15; i++) {
rabbitTemplate.convertAndSend("order_exchange", "info", "hello world" + i);
}
Thread.sleep(2000);
}
5、查看可视化界面,进入死信队列的是时间最早的(也就是最先发送的)
6、定义消费者
package com.example.listener05;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class ConsumerListener01 {
@RabbitListener(queues = "dlx_queue")
public void listener01(Message message) {
String msg = new String(message.getBody());
System.out.println("接收到的死信队列消息:" + msg);
}
@RabbitListener(queues = "order_queue")
public void listener02(Message message) {
String msg = new String(message.getBody());
System.out.println("接收到的订单队列消息:" + msg);
}
}
1、在rabbitmq
中没有真正意义上的延迟队列任务,只是采用ttl
+死信队列来完成的
2、延迟任务主要用于场景
3、延迟任务的结构图
4、创建一个延迟任务的队列
@Configuration
public class RabbitDlxExchangeConfiguration {
...
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order_queue").
// maxLength(10).
ttl(2000). // 过期时间
deadLetterExchange("dlx_exchange"). // 死信队列的交换机
deadLetterRoutingKey("dead"). // 死信队列的routingKey
build();
}
}
5、发送消息,观察可视化界面,时间到了就会进入到死信队列中
@Test
public void test06() throws InterruptedException {
rabbitTemplate.convertAndSend("order_exchange", "info", "hello world");
Thread.sleep(2000);
}
6、在死信队列中监听数据来改变数据库状态