先来个拓展命令吧
- docker 删除rabbitmq队列命令
#进入docker容器
docker exce -it rabbitmq bash
#停止rabbitmq运行
rabbitmqctl stop_app # 可以双击table键查看命令
#执行删除队列命令
rabbitmqctl reset
#启动rabbitmq
rabbitctl start_app
rabbitmq在什么情况下可能会丢失消息呢?
-
图解:

- 消息可能丢失的情况
1: 消息在发送的时候没有到达交换机2: 消息从交换机路由到队列失败 -
生产者
-
解决方案:
1: 添加confirm确认 ---> 可以解决: 消息在发送的过程中没有到达交换机2: 添加return退回模式 ---> 可以解决: 消息从交换机路由到队列失败- 代码 :
配置文件:
spring:
application:
name: rabbitmq-day02
rabbitmq:
host: 192.168.200.130
port: 5672
username: guest
password: guest
virtual-host: /
publisher-confirms: true
publisher-returns: true
server:
port: 8083
config包:
@Configuration
public class QueueConfig {
/**
* @program: day10
*
* @description:
*
* @author: Mr.Li
*
* @create: 2020-07-27 16:24
**/
// 创建队列
@Bean
public Queue createdDirectQueue(){
return new Queue("directQueue", true, false, false);
}
// 创建路由
@Bean
public DirectExchange createdDirectExchange(){
return new DirectExchange("directExchange", true, false);
}
// 绑定
@Bean
Binding createdDirectQueueBingcreatedDirectExchange(Queue createdDirectQueue, DirectExchange createdDirectExchange) {
return BindingBuilder.bind(createdDirectQueue).to(createdDirectExchange).with("itheima");
}
}
-------------------------------------------------
@Slf4j
@Component
@RabbitListener(queues = "directQueue")
public class RabbitConfirmCallbackAndReturnCallback implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
/**
* @program: day10
* @description:
* @author: Mr.Li
* @create: 2020-07-27 17:00
**/
@Autowired
private RabbitTemplate template; // 这里为什么从ioc容器 获取RabbitTemplate 对象呢 因为他需要使用的RabbitTemplate对象必须和生产者使用的是同一个对象
@PostConstruct // 这个注解的意思就是 在 Autowired 这个注解之后执行
public void init() {
template.setConfirmCallback(this::confirm); // 这里的this::confirm 也可以写成 this 会自动匹配
template.setReturnCallback(this::returnedMessage);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
log.info("消息到达交换机: {} ", ack);
} else {
log.info("消息未到达交换机 - 原因 : {} ", cause);
}
}
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
log.info("退回信息 : message {} , replyCode : {} , replyText: {} , exchange : {} , routingKey : {} ",
message, replyCode, replyText, exchange, routingKey);
}
}
消费者:
@Component
@Slf4j
@RabbitListener(queues = "directQueue")
public class QueueConsumer {
/**
* @program: day10
* @description:
* @author: Mr.Li
* @create: 2020-07-27 16:47
**/
@RabbitHandler
public void monitorDirectQueue(String argument) {
log.info("监听到信息: {} ", argument);
}
}
生产者:
@RestController
@Slf4j
public class ProducerController {
/**
* @program: day10
* @description:
* @author: Mr.Li
* @create: 2020-07-27 16:40
**/
@Autowired
private RabbitTemplate template;
@GetMapping("/directQueue/{directExchange}/{itheima}/{argument}")
public void sendDirectExchange(@PathVariable("directExchange") String directExchange,
@PathVariable("itheima") String itheima,
@PathVariable("argument") String argument) {
template.convertAndSend(directExchange, itheima, argument);
}
}
-
小结:
-
在配置文件中开启confirm确认模式以及return退回模式
-
实现RabbitTemplate.ConfirmCallBack接口
- 重写confirm方法: 不论消息是否到达该交换机都会被调用
-
实现RabbitTemplate.ReturnCallBack
- 重写returnedMessage方法 只有消息从交换机路由到队列失败才会执行
-
将confirm和returnedMessage设置到RabbitmqTemplate中,其中使用了@PostConstruct注解表示在 @Autowired后执行
-
这里 为什么要把confirm和returnedMessage放到RabbitTemplate中
原因: 因为只有把confirm和returnedMessage放到RabbitTemplate中confirm和returnedMessage的方法才会生效
-
-
消费者消息确认
- 消息确认的三种方式:
1: 自动确认(默认z设置): 消息消费成功,消息会从队列中删除,消费失败会重新回到队列再次进行消费 最终会出现 --- > 死循环现象
2: 手动确认: 消息需要消费者自己通过业务编码实现,根据自己的实际情况解决是否从队列删除
3: none: 不做任何操作 消费只会被消费一次,成功就成功,失败就从队列删除
* 自动配置的配置文件
spring:
application:
name: rabbitmq-day02
rabbitmq:
host: 192.168.200.130
port: 5672
username: guest
password: guest
virtual-host: /
publisher-confirms: true
publisher-returns: true
listener:
simple:
acknowledge-mode: manual # 开启手动确认模式
server:
port: 8083
消费者代码:
@Component
@Slf4j
@RabbitListener(queues = "directQueue")
public class QueueConsumer {
/**
* @program: day10
* @description:
* @author: Mr.Li
* @create: 2020-07-27 16:47
**/
@RabbitHandler
public void monitorDirectQueue(String argument, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, @Header(AmqpHeaders.REDELIVERED) boolean redelivered) {
try {
log.info("消息消费成功");
if (argument.contains("联想")) {
throw new RuntimeException("不能购买联想品牌");
}
log.info("下单消息; {} ", argument);
// 删除消息
channel.basicAck(deliveryTag,false);
} catch (IOException e) {
e.printStackTrace();
// 判断消息是否回滚过
if (redelivered){
// 删除
log.info("消息执行删除操作");
try {
channel.basicNack(deliveryTag, false, false);
} catch (IOException e1) {
e1.printStackTrace();
}
}else {
// 回滚
log.info("消息回滚");
try {
channel.basicNack(deliveryTag, false, true);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
配置文件中需要手动修改为手动确认
-
限流
- 限流只需要修改配置文件

- 限流只需要修改配置文件
-
延时队列
-
过期队列--TTL
官方文档: https://www.rabbitmq.com/ttl.html#per-queue-message-ttl -
死信
注意: 队列一旦创建不能进行修改,如要进行修改请删除重新创建
创建死信交换机的目的: 没有交换机的时候删除消息是直接删除,绑定了交换机他会将删除的消息保存到交换机对应的队列中
死信交换机创建必须在普通交换机之前否则会报错
- 过期队列 + 死信 = 延时队列
-
-
消息幂等问题
-
rabbitmq - docker集群的搭建
- 创建第一台命令
docker run -id --name=rabbitmq1 --hostname rabbitmq1 -e RABBITMQ_ERLANG_COOKIE='rabbitmq' -p 5672:5672 -p 15672:15672 rabbitmq:management
- 创建第二台命令
docker run -id --name=rabbitmq2 --hostname rabbitmq2 -e RABBITMQ_ERLANG_COOKIE='rabbitmq' -p 5673:5672 -p 15673:15672 --link rabbitmq1:rabbitmq1 rabbitmq:management
- 创建第三台代码
docker run -id --name=rabbitmq3 --hostname rabbitmq3 -e RABBITMQ_ERLANG_COOKIE='rabbitmq' -p 5674:5672 -p 15674:15672 --link rabbitmq1:rabbitmq1 --link rabbitmq2:rabbitmq2 rabbitmq:management
#3.进入容器内部设置
#myrabbitmq1操作
#进入容器内部
docker exec -it myrabbitmq1 bash
#停止rabbitmq服务
rabbitmqctl stop_app
#清空rabbitmq交换机和队列---》恢复出厂设置
rabbitmqctl reset
#启动rabbitmq服务
rabbitmqctl start_app
#退出
exit
#myrabbitmq2操作
#进入容器内部
docker exec -it myrabbitmq2 bash
#停止rabbitmq服务
rabbitmqctl stop_app
#清空rabbitmq交换机和队列---》恢复出厂设置
rabbitmqctl reset
#连接集群,将rabbitmq2设置为内存节点
rabbitmqctl join_cluster --ram rabbit@rabbitmq1
#启动rabbitmq服务
rabbitmqctl start_app
#退出
exit
#myrabbitmq3操作
#进入容器内部
docker exec -it myrabbitmq3 bash
#停止rabbitmq服务
rabbitmqctl stop_app
#清空rabbitmq交换机和队列---》恢复出厂设置
rabbitmqctl reset
#连接集群,将rabbitmq3设置为磁盘
rabbitmqctl join_cluster rabbit@rabbitmq1
#启动rabbitmq服务
rabbitmqctl start_app
#退出
exit
#在任意一个容器中可以查看集群状态
rabbitmqctl cluster_status
#为了解决集群中单节点故障后队列依然可以正常使用,这里给所有的队列设置为镜像队列
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
#注意事项
mq集群中至少需要一台磁盘节点
修改配置文件

-
搭建集群的主要目的:
- 1: 防止单点故障
- 2: 实现高可用 , 高并发
- 3: 实现单台容量的扩充
-
消息幂等性问题
- 解决方案: 加锁即可