RabbitMQ发布确认及备份交换机

可以通过设置RabbitMQ的发布确认和失败回退功能来确认消息是否成功发布。

也可以为交换机设置备份交换机,来接收不可路由的消息。

demo结构

配置及实现

application.yml

server:
port: 8080 spring:
rabbitmq:
host: rabbitmq
port: 5672
username: admin
password: admin
publisher-confirm-type: correlated #设置发布者确认,调用RabbitTemplate.ConfirmCallback.confirm方法
publisher-returns: true # 交换机无法路由消息时回退给生产者,调用RabbitTemplate.ReturnCallback.returnedMessage方法

交换机及队列配置

package com.zjw.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* 发布确认SpringBoot
*/
@Configuration
public class ConfirmConfig { public static final String CONFIRM_EXCHANGE_NAME = "confirm.exchange";
public static final String CONFIRM_QUEUE_NAME = "confirm.queue";
public static final String BACKUP_EXCHANGE_NAME = "backup.exchange";
public static final String BACKUP_QUEUE_NAME = "backup.queue";
public static final String WARNING_QUEUE_NAME = "warning.queue"; /**
* 创建发布确认交换机
* @return 交换机
*/
@Bean("confirmExchange")
public DirectExchange confirmExchange(){
ExchangeBuilder builder = ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME)
.durable(true)
//设置交换机的备份交换机
.withArgument("alternate-exchange", BACKUP_EXCHANGE_NAME);
return builder.build();
} /**
* 创建备份交换机
* @return
交换机
*/
@Bean("backupExchange")
public FanoutExchange backupExchange(){
return new FanoutExchange(BACKUP_EXCHANGE_NAME);
} /**
* 创建发布确认队列
* @return 队列
*/
@Bean("confirmQueue")
public Queue confirmQueue(){
return QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();
} /**
* 创建备份队列
* @return 队列
*/
@Bean("backupQueue")
public Queue backupQueue(){
return QueueBuilder.durable(BACKUP_QUEUE_NAME).build();
} /**
* 创建告警队列
* @return 队列
*/
@Bean("warningQueue")
public Queue warningQueue(){
return QueueBuilder.durable(WARNING_QUEUE_NAME).build();
} /**
* 绑定发布确认队列和交换机
* @param queue 队列
* @param exchange 交换机
* @return 绑定关系
*/
@Bean
public Binding queueBinding(@Qualifier("confirmQueue") Queue queue,
@Qualifier("confirmExchange") DirectExchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("key1");
} /**
* 绑定备份队列和备份交换机
* @param queue 队列
* @param exchange 交换机
* @return 绑定关系
*/
@Bean
public Binding backupBinding(@Qualifier("backupQueue") Queue queue,
@Qualifier("backupExchange") FanoutExchange exchange){
return BindingBuilder.bind(queue).to(exchange);
} /**
* 绑定告警队列和备份交换机
* @param queue 队列
* @param exchange 交换机
* @return 绑定关系
*/
@Bean
public Binding warningBinding(@Qualifier("warningQueue") Queue queue,
@Qualifier("backupExchange") FanoutExchange exchange){
return BindingBuilder.bind(queue).to(exchange);
}
}

生产者配置

package com.zjw.controller;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 发布确认生产者
*/
@AllArgsConstructor
@RestController
@RequestMapping("/confirm")
@Slf4j
public class SendConfirmMsgController { public static final String CONFIRM_EXCHANGE_NAME = "confirm.exchange"; private final RabbitTemplate rabbitTemplate; @GetMapping("sendMessage/{message}")
public void sendMessage(@PathVariable String message){
//指定消息id为1
CorrelationData correlationData1 = new CorrelationData("1");
String routingKey = "key1";
rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME, routingKey, message+routingKey, correlationData1); CorrelationData correlationData2 = new CorrelationData("2");
routingKey = "key2";
rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME, routingKey, message+routingKey, correlationData2); log.info("发送消息内容:{}", message);
}
}

这里我使用了不同的routingKey来实现将消息发送到不同的队列及备份交换机中。

交换机回调方法

package com.zjw.config;

import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component; /**
* 交换机的回调接口
*/
@AllArgsConstructor
@Component
@Slf4j
public class MyCallBack implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback { private final RabbitTemplate rabbitTemplate; @PostConstruct
public void init() {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnsCallback(this);
} /**
* 交换机不管是否收到消息的一个回调方法。
* @param correlationData 回调的关联数据。
* @param ack ACK 为 true,nack 为 false
* @param cause 可选原因,用于 nack,如果可用,否则为 null。
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String id = correlationData != null ? correlationData.getId() : "";
if (ack) {
log.info("交换机收到id为{}的消息",id);
} else {
log.info("交换机未确认收到id为{}的消息,由于原因{}", id, cause);
}
} /**
* Returned message callback.
* 如果通过延迟队列发送的消息,由于消息是在延迟交换机中,还没有到达延迟队列,也会被交换机调用退回方法,不过等待消息到达延迟时间后会再发送到队列中
* 如果交换机是发布确认,但是设置了备份交换机,消息会被发送到备份交换机中,不会被退回。如果没有备份交换机,消息会被退回。
* @param returned the returned message and metadata.
*/
@Override
public void returnedMessage(ReturnedMessage returned) {
log.warn("消息:{},被交换机退回:{},退回原因:{},路由Key:{}",
new String(returned.getMessage().getBody()),
returned.getExchange(),
returned.getReplyText(),
returned.getRoutingKey());
}
}

这里有个问题,由于rabbitTemplate设置的是单例的,如果向其他交换机发送消息的时候也会被调用到回调方法,使用的时候需要注意下。

消费者

我这里开启了两个消费者,一个用来消费confirm.queue,一个用来消费warning.queue

package com.zjw.consumer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component; /**
* 确认队列消费者,消费确认队列消息
*/
@Component
@Slf4j
public class ConfirmConsumer { public static final String CONFIRM_QUEUE_NAME = "confirm.queue"; @RabbitListener(queues = CONFIRM_QUEUE_NAME)
public void receiveMsg(Message message){
String msg = new String(message.getBody());
log.info("接受到队列confirm.queue消息:{}", msg);
}
}
package com.zjw.consumer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component; /**
* 告警消费者,消费告警队列消息
*/
@Component
@Slf4j
public class WarningConsumer { public static final String WARNING_QUEUE_NAME = "warning.queue"; @RabbitListener(queues = WARNING_QUEUE_NAME)
public void receiveMsg(Message message){
String msg = new String(message.getBody());
log.info("告警消费者到队列warning.queue消息:{}", msg);
}
}

测试

通过浏览器访问 http://localhost:8080/confirm/sendMessage/hello ,发送消息

生产者日志

2024-01-11T18:57:19.643+08:00  INFO 19184 --- [io-8080-exec-10] c.z.controller.SendConfirmMsgController  : 发送消息内容:hello
2024-01-11T18:57:19.652+08:00 INFO 19184 --- [ectionFactory19] com.zjw.config.MyCallBack : 交换机收到id为2的消息
2024-01-11T18:57:19.652+08:00 INFO 19184 --- [ectionFactory18] com.zjw.config.MyCallBack : 交换机收到id为1的消息

发现调用了confirm的回调方法。

至于returnedMessage回调为什么没有被调用,即使没有key2routingKey,是因为我这里设置了备份的交换机,消息被发送到了备份的交换机backup.exchange,进而被送到了backup.queuewarning.queue队列。

消费者日志

2024-01-11T18:57:19.647+08:00  INFO 3368 --- [ntContainer#0-1] com.zjw.consumer.ConfirmConsumer         : 接受到队列confirm.queue消息:hellokey1
2024-01-11T18:57:19.647+08:00 INFO 3368 --- [ntContainer#4-1] com.zjw.consumer.WarningConsumer : 告警消费者到队列warning.queue消息:hellokey2

可以看到消息1进入了confirm.queue,消息2进入了warning.queue。也可以通过管理界面看到backup.queue也有我们的消息2.

RabbitMQ发布确认及备份交换机的更多相关文章

  1. RabbitMq发布确认

    RabbitMq发布确认 发布确认原理 生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被 ...

  2. RabbitMQ(二):交换机

    前言 学习自bili尚硅谷-RabbitMQ 发布确认 之前的消息应答,队列持久化是为了保证 -> 消息从rabbitmq队列到消费者的过程中不会丢失:消息持久化则是为了保证 -> 消息从 ...

  3. rabbitMq及安装、fanout交换机-分发(发布/订阅)

    <dependency>            <groupId>com.rabbitmq</groupId>            <artifactId& ...

  4. rabbitmq的发布确认和事务 - 2207872494的个人空间

    rabbitmq的发布确认和事务 - 2207872494的个人空间   https://my.oschina.net/lzhaoqiang/blog/670749

  5. RabbitMQ 备份交换机(alternate-exchange)介绍

    RabbitMQ之备份交换机(alternate-exchange) 1.备份交换器,AlternateExchange(AE) 备份交换器是为了实现没有路由到队列的消息,声明交换机的时候添加属性al ...

  6. 9. RabbitMQ系列之消息发布确认

    Publisher Confirms发布确认是用于实现可靠发布的RabbitMQ扩展. 我们将使用发布确认来确保已发布的消息已安全到达代理.我们将介绍几种使用publisher确认的策略,并解释其优缺 ...

  7. 【转载】rabbitmq的发布确认和事务

    地址:https://my.oschina.net/lzhaoqiang/blog/670749 摘要: 介绍confirm的工作机制.使用spring-amqp介绍事务以及发布确认的使用方式.因为事 ...

  8. rabbitmq的发布确认和事务

    摘要: 介绍confirm的工作机制.使用spring-amqp介绍事务以及发布确认的使用方式.因为事务以及发布确认是针对channel来讲,所以在一个连接中两个channel,一个channel可以 ...

  9. rabbitMq可靠消息投递之交换机备份

    //备份队列 @Bean("alternate_queue") public Queue alternate_queue() { return new Queue("al ...

  10. RabbitMQ 消息确认机制以及lazy queue+ disk消息持久化

    一:Basic的一些属性,一些方法 1. 消费端的确认 自动确认: message出队列的时候就自动确认[broke] basicget... 手工确认: message出队列之后,要应用程序自己去确 ...

随机推荐

  1. Q:查看锁表进程,及杀死所有锁表进程sql

     查看锁表进程 SELECT DISTINCT decode(s.inst_id, 1, 'DB1', 2, 'DB2') 数据库服务器, decode(s.BLOCKING_SESSION, '', ...

  2. 5. Docker 本地镜像发布到阿里云

    5. Docker 本地镜像发布到阿里云 @ 目录 5. Docker 本地镜像发布到阿里云 1. 本地镜像发布到阿里云流程 最后: 1. 本地镜像发布到阿里云流程 镜像的生成方法: 基于当前容器创建 ...

  3. 为什么TCP需要三次握手?深入解析背后的设计哲学

    在互联网通信中,TCP(传输控制协议)是确保数据可靠传输的基石.而TCP连接的建立过程--"三次握手"(Three-Way Handshake),看似简单的三个步骤,却蕴含了网络协 ...

  4. JUC并发—1.Java集合包底层源码剖析

    大纲 1.为什么要对JDK源码剖析 2.ArrayList源码一:基本原理以及优缺点 3.ArrayList源码二:核心方法的原理 4.ArrayList源码三:数组扩容以及元素拷贝 5.Linked ...

  5. 学习vue ,环境搭建(VS code、node.js、cnpm、vue-cli)创建项目 并引入element

    安装 1.Visual studio code(VS code) 往期博客:https://www.cnblogs.com/technicist/p/12677052.html 2.Node.js与c ...

  6. 【博客搭建】Latex数学书写笔记

    [博客搭建]Latex 数学书写笔记 Latex 是一种文档书写语言,支持大量的特殊字符,包括书写数学公式,并且很多 Markdown 环境都支持该语言. 布局实现 靠左:使用内联数学块$...$. ...

  7. 如何在 PIP 配置文件中设置默认源?

    在不同的操作系统中,在 PIP 配置文件中设置默认源的方法如下: Windows 操作系统 打开文件资源管理器,在地址栏输入 %APPDATA% 并回车,进入用户配置目录. 在该目录下创建一个名为 p ...

  8. 傻妞教程——对接QQ频道机器人

    安装插件 前往傻妞插件市场安装QQ频道机器人插件,基于Node开发. 申请机器人 使用前请先确保已在机器人平台创建机器人 (opens new window),具体创建教程在超链接里面有,根据教程图以 ...

  9. AGC015D题解

    简要题意 给定一个区间 \([l,r]\),从中选出若干整数按位或,求可能出现的数的方案数. 数据范围:\(1\le l\le r\le2^{60}\). 思路 首先对于 \([l,r]\) 里的数全 ...

  10. AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现

    引言 在当今这个数据爆炸的时代,信息的快速存储与高效检索已经成为技术领域的核心挑战.随着人工智能(AI)和机器学习(ML)的迅猛发展,向量存储和相似性搜索技术逐渐崭露头角,成为处理海量数据的利器.对于 ...