目录
一、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
二、代码
1、死信队列
1.1、TTL过期
1.1.1、RabbitMQ配置类
@Configuration
public class RabbitMQConfig11 {
// 普通交换机
public static final String COMMON_EXCHANGE_NAME = "common_exchange11";
// 死信交换机
public static final String DEAD_EXCHANGE_NAME = "dead_exchange11";
// 普通队列
public static final String COMMON_QUEUE_NAME = "common_queue11";
// 死信队列
public static final String DEAD_QUEUE_NAME = "dead_queue11";
// 普通交换机
@Bean
public Exchange commonExchange11() {
return new DirectExchange(COMMON_EXCHANGE_NAME);
}
// 死信交换机
@Bean
public Exchange deadExchange11() {
return new DirectExchange(DEAD_EXCHANGE_NAME);
}
// 普通队列
@Bean
public Queue commonQueue11() {
return QueueBuilder.durable(COMMON_QUEUE_NAME).deadLetterExchange(DEAD_EXCHANGE_NAME).deadLetterRoutingKey(DEAD_QUEUE_NAME).ttl(10000).build();
}
// 死信队列
@Bean
public Queue deadQueue11() {
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
// 绑定普通交换机和普通队列
@Bean
public Binding commonBinding11(@Qualifier("commonQueue11") Queue commonQueue, @Qualifier("commonExchange11") Exchange commonExchange) {
return BindingBuilder.bind(commonQueue).to(commonExchange).with(COMMON_QUEUE_NAME).noargs();
}
// 绑定死信交换机和死信队列
@Bean
public Binding deadBinding11(@Qualifier("deadQueue11") Queue deadQueue, @Qualifier("deadExchange11") Exchange deadExchange) {
return BindingBuilder.bind(deadQueue).to(deadExchange).with(DEAD_QUEUE_NAME).noargs();
}
}
1.1.2、生产者
@RestController
public class Provider11 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg11")
public String sendMsg() {
rabbitTemplate.convertAndSend(RabbitMQConfig11.COMMON_EXCHANGE_NAME, RabbitMQConfig11.COMMON_QUEUE_NAME, "测试消息");
return "发送成功";
}
}
1.1.3、消费者
@Component
public class Consumer11 {
@RabbitListener(queues = {RabbitMQConfig11.DEAD_QUEUE_NAME})
public void receiveMsg(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("消息内容:" + msg);
}
}
1.2、消息数量超过队列最大长度
1.2.1、RabbitMQ配置类
@Configuration
public class RabbitMQConfig12 {
// 普通交换机
public static final String COMMON_EXCHANGE_NAME = "common_exchange12";
// 死信交换机
public static final String DEAD_EXCHANGE_NAME = "dead_exchange12";
// 普通队列
public static final String COMMON_QUEUE_NAME = "common_queue12";
// 死信队列
public static final String DEAD_QUEUE_NAME = "dead_queue12";
// 普通交换机
@Bean
public Exchange commonExchange12() {
return new DirectExchange(COMMON_EXCHANGE_NAME);
}
// 死信交换机
@Bean
public Exchange deadExchange12() {
return new DirectExchange(DEAD_EXCHANGE_NAME);
}
// 普通队列
@Bean
public Queue commonQueue12() {
return QueueBuilder.durable(COMMON_QUEUE_NAME).deadLetterExchange(DEAD_EXCHANGE_NAME).deadLetterRoutingKey(DEAD_QUEUE_NAME).maxLength(6).build();
}
// 死信队列
@Bean
public Queue deadQueue12() {
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
// 绑定普通交换机和普通队列
@Bean
public Binding commonBinding12(@Qualifier("commonQueue12") Queue commonQueue, @Qualifier("commonExchange12") Exchange commonExchange) {
return BindingBuilder.bind(commonQueue).to(commonExchange).with(COMMON_QUEUE_NAME).noargs();
}
// 绑定死信交换机和死信队列
@Bean
public Binding deadBinding12(@Qualifier("deadQueue12") Queue deadQueue, @Qualifier("deadExchange12") Exchange deadExchange) {
return BindingBuilder.bind(deadQueue).to(deadExchange).with(DEAD_QUEUE_NAME).noargs();
}
}
1.2.2、生产者
@RestController
public class Provider12 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg12")
public String sendMsg() {
for (int i = 1; i <= 10; i++) {
rabbitTemplate.convertAndSend(RabbitMQConfig12.COMMON_EXCHANGE_NAME, RabbitMQConfig12.COMMON_QUEUE_NAME, "测试消息" + i);
}
return "发送成功";
}
}
1.2.3、消费者
@Component
public class Consumer12 {
@RabbitListener(queues = {RabbitMQConfig12.DEAD_QUEUE_NAME})
public void receiveMsg2(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("死信队列接收到的消息内容:" + msg);
}
}
1.3、消费者拒绝接收消息,并拒绝将消息重新放回队列
1.3.1、RabbitMQ配置类
@Configuration
public class RabbitMQConfig13 {
// 普通交换机
public static final String COMMON_EXCHANGE_NAME = "common_exchange13";
// 死信交换机
public static final String DEAD_EXCHANGE_NAME = "dead_exchange13";
// 普通队列
public static final String COMMON_QUEUE_NAME = "common_queue13";
// 死信队列
public static final String DEAD_QUEUE_NAME = "dead_queue13";
// 普通交换机
@Bean
public Exchange commonExchange13() {
return new DirectExchange(COMMON_EXCHANGE_NAME);
}
// 死信交换机
@Bean
public Exchange deadExchange13() {
return new DirectExchange(DEAD_EXCHANGE_NAME);
}
// 普通队列
@Bean
public Queue commonQueue13() {
return QueueBuilder.durable(COMMON_QUEUE_NAME).deadLetterExchange(DEAD_EXCHANGE_NAME).deadLetterRoutingKey(DEAD_QUEUE_NAME).build();
}
// 死信队列
@Bean
public Queue deadQueue13() {
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
// 绑定普通交换机和普通队列
@Bean
public Binding commonBinding13(@Qualifier("commonQueue13") Queue commonQueue, @Qualifier("commonExchange13") Exchange commonExchange) {
return BindingBuilder.bind(commonQueue).to(commonExchange).with(COMMON_QUEUE_NAME).noargs();
}
// 绑定死信交换机和死信队列
@Bean
public Binding deadBinding13(@Qualifier("deadQueue13") Queue deadQueue, @Qualifier("deadExchange13") Exchange deadExchange) {
return BindingBuilder.bind(deadQueue).to(deadExchange).with(DEAD_QUEUE_NAME).noargs();
}
}
1.3.2、生产者
@RestController
public class Provider13 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg13")
public String sendMsg() {
rabbitTemplate.convertAndSend(RabbitMQConfig13.COMMON_EXCHANGE_NAME, RabbitMQConfig13.COMMON_QUEUE_NAME, "测试消息");
return "发送成功";
}
}
1.3.3、消费者
@Component
public class Consumer13 {
@RabbitListener(queues = {RabbitMQConfig13.COMMON_QUEUE_NAME})
public void receiveMsg(Message message, Channel channel) throws IOException {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
}
@RabbitListener(queues = {RabbitMQConfig13.DEAD_QUEUE_NAME})
public void receiveMsg2(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("死信队列接收到的消息内容:" + msg);
}
}
2、优先级队列
2.1、RabbitMQ配置类
@Configuration
public class RabbitMQConfig5 {
// 普通交换机
public static final String COMMON_EXCHANGE_NAME = "common_exchange5";
// 普通队列
public static final String COMMON_QUEUE_NAME = "common_queue5";
// 普通交换机
@Bean
public Exchange commonExchange5() {
return new DirectExchange(COMMON_EXCHANGE_NAME);
}
// 普通队列
@Bean
public Queue commonQueue5() {
return QueueBuilder.durable(COMMON_QUEUE_NAME).withArgument("x-max-priority", 10).build();
}
// 绑定普通交换机和普通队列
@Bean
public Binding commonBinding5(@Qualifier("commonQueue5") Queue commonQueue, @Qualifier("commonExchange5") Exchange commonExchange) {
return BindingBuilder.bind(commonQueue).to(commonExchange).with(COMMON_QUEUE_NAME).noargs();
}
}
2.2、生产者
@RestController
public class Provider5 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg5")
public String sendMsg() {
for (int i = 1; i <= 10; i++) {
if (i == 5) {
rabbitTemplate.convertAndSend(RabbitMQConfig5.COMMON_EXCHANGE_NAME, RabbitMQConfig5.COMMON_QUEUE_NAME, "测试消息" + i, msg -> {
msg.getMessageProperties().setPriority(5);
return msg;
});
} else {
rabbitTemplate.convertAndSend(RabbitMQConfig5.COMMON_EXCHANGE_NAME, RabbitMQConfig5.COMMON_QUEUE_NAME, "测试消息" + i);
}
}
return "发送成功";
}
}
2.3、消费者
@Component
public class Consumer5 {
@RabbitListener(queues = {RabbitMQConfig5.COMMON_QUEUE_NAME})
public void receiveMsg(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("消息内容:" + msg);
}
}
3、自定义延迟交换机
3.1、RabbitMQ配置类
@Configuration
public class RabbitMQConfig2 {
// 延时交换机
public static final String DELAY_EXCHANGE_NAME = "delay_exchange2";
// 普通队列
public static final String COMMON_QUEUE_NAME = "common_queue2";
// 延时交换机
@Bean
public Exchange delayExchange2() {
Map<String, Object> arguments = new HashMap<>(1);
arguments.put("x-delayed-type", "direct");
return new CustomExchange(DELAY_EXCHANGE_NAME, "x-delayed-message", true, false, arguments);
}
// 普通队列
@Bean
public Queue commonQueue2() {
return QueueBuilder.durable(COMMON_QUEUE_NAME).build();
}
// 绑定延时交换机和普通队列
@Bean
public Binding commonBinding2(@Qualifier("commonQueue2") Queue commonQueue, @Qualifier("delayExchange2") Exchange delayExchange) {
return BindingBuilder.bind(commonQueue).to(delayExchange).with(COMMON_QUEUE_NAME).noargs();
}
}
3.2、生产者
@RestController
public class Provider2 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg2")
public String sendMsg() {
rabbitTemplate.convertAndSend(RabbitMQConfig2.DELAY_EXCHANGE_NAME, RabbitMQConfig2.COMMON_QUEUE_NAME, "我是延时时间是10s的消息", msg -> {
msg.getMessageProperties().setDelay(10000);
return msg;
});
rabbitTemplate.convertAndSend(RabbitMQConfig2.DELAY_EXCHANGE_NAME, RabbitMQConfig2.COMMON_QUEUE_NAME, "我是延时时间是2s的消息", msg -> {
msg.getMessageProperties().setDelay(2000);
return msg;
});
return "发送成功";
}
}
3.3、消费者
@Component
public class Consumer2 {
@RabbitListener(queues = {RabbitMQConfig2.COMMON_QUEUE_NAME})
public void receiveMsg(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("消息内容:" + msg);
}
}
4、消息无法抵达交换机或者无法抵达队列的情况
4.1、application.properties配置
# 开启消息到交换机的发布确认
spring.rabbitmq.publisher-confirm-type=correlated
# 开启发布者回退,即消息无法投递到消费者,然后将消息回退给生产者
spring.rabbitmq.publisher-returns=true
4.2、RabbitMQ配置类
@Configuration
public class RabbitMQConfig3 {
// 普通交换机
public static final String COMMON_EXCHANGE_NAME = "common_exchange3";
// 普通队列
public static final String COMMON_QUEUE_NAME = "common_queue3";
// 普通交换机
@Bean
public Exchange commonExchange3() {
return ExchangeBuilder.directExchange(COMMON_EXCHANGE_NAME).build();
}
// 普通队列
@Bean
public Queue commonQueue3() {
return QueueBuilder.durable(COMMON_QUEUE_NAME).build();
}
// 绑定普通交换机和普通队列
@Bean
public Binding commonBinding3(@Qualifier("commonQueue3") Queue commonQueue, @Qualifier("commonExchange3") Exchange commonExchange) {
return BindingBuilder.bind(commonQueue).to(commonExchange).with(COMMON_QUEUE_NAME).noargs();
}
}
4.3、RabbitMQ回调类
@Component
public class RabbitmqCallback3 implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
// rabbitTemplate被注入之后被调用,进行对象注入
@PostConstruct
public void init() {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnCallback(this);
}
// 无论消息发送到交换机成功或者失败,都会回调该方法,前提是在application.properties中配置:spring.rabbitmq.publisher-confirm-type=correlated
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
try {
String id = correlationData.getId();
System.out.printf("交换机接收消息反馈,其中id:%s,接收成功:%b,失败原因:%s\n", id, ack, cause);
} catch (Exception e) {
}
}
// 当交换机发送消息到队列失败的时候,将会回调该方法,成功不会回调,前提是在application.properties中配置:spring.rabbitmq.publisher-returns=true
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.printf("交换机发送消息到队列失败,其中消息内容:%s,回复码:%d,回复内容:%s,交换机:%s,路由:%s\n", new String(message.getBody(), StandardCharsets.UTF_8), replyCode, replyText, exchange, routingKey);
}
}
4.4、生产者
@RestController
public class Provider3 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg3")
public String sendMsg() {
// 测试正常消息
CorrelationData correlationData1 = new CorrelationData();
correlationData1.setId("1");
rabbitTemplate.convertAndSend(RabbitMQConfig3.COMMON_EXCHANGE_NAME, RabbitMQConfig3.COMMON_QUEUE_NAME, "测试消息1", correlationData1);
// 测试消息无法到交换机
CorrelationData correlationData2 = new CorrelationData();
correlationData2.setId("2");
rabbitTemplate.convertAndSend(RabbitMQConfig3.COMMON_EXCHANGE_NAME + "1", RabbitMQConfig3.COMMON_QUEUE_NAME, "测试消息2", correlationData2);
// 测试消息无法路由到队列
CorrelationData correlationData3 = new CorrelationData();
correlationData3.setId("3");
rabbitTemplate.convertAndSend(RabbitMQConfig3.COMMON_EXCHANGE_NAME, RabbitMQConfig3.COMMON_QUEUE_NAME + "1", "测试消息3", correlationData3);
return "发送成功";
}
}
4.5、消费者
@Component
public class Consumer3 {
@RabbitListener(queues = {RabbitMQConfig3.COMMON_QUEUE_NAME})
public void receiveMsg(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("消息内容:" + msg);
}
}
5、备用交换机
5.1、RabbitMQ配置类
@Configuration
public class RabbitMQConfig4 {
// 普通交换机
public static final String COMMON_EXCHANGE_NAME = "common_exchange4";
// 备用交换机
public static final String BACKUP_EXCHANGE_NAME = "backup_exchange4";
// 普通队列
public static final String COMMON_QUEUE_NAME = "common_queue4";
// 备用队列
public static final String BACKUP_QUEUE_NAME = "backup_queue4";
// 普通交换机
@Bean
public Exchange commonExchange4() {
return ExchangeBuilder.directExchange(COMMON_EXCHANGE_NAME).withArgument("alternate-exchange", BACKUP_EXCHANGE_NAME).build();
}
// 备用交换机
@Bean
public FanoutExchange backupExchange4() {
return ExchangeBuilder.fanoutExchange(BACKUP_EXCHANGE_NAME).build();
}
// 普通队列
@Bean
public Queue commonQueue4() {
return QueueBuilder.durable(COMMON_QUEUE_NAME).build();
}
// 备用队列
@Bean
public Queue backupQueue4() {
return QueueBuilder.durable(BACKUP_QUEUE_NAME).build();
}
// 绑定普通交换机和普通队列
@Bean
public Binding commonBinding4(@Qualifier("commonQueue4") Queue commonQueue, @Qualifier("commonExchange4") Exchange commonExchange) {
return BindingBuilder.bind(commonQueue).to(commonExchange).with(COMMON_QUEUE_NAME).noargs();
}
// 绑定备用交换机和备用队列
@Bean
public Binding backupBinding4(@Qualifier("backupQueue4") Queue backupQueue, @Qualifier("backupExchange4") FanoutExchange backupExchange) {
return BindingBuilder.bind(backupQueue).to(backupExchange);
}
}
5.2、生产者
@RestController
public class Provider4 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg4")
public String sendMsg() {
rabbitTemplate.convertAndSend(RabbitMQConfig4.COMMON_EXCHANGE_NAME, RabbitMQConfig4.COMMON_QUEUE_NAME, "测试消息1");
rabbitTemplate.convertAndSend(RabbitMQConfig4.COMMON_EXCHANGE_NAME, RabbitMQConfig4.COMMON_QUEUE_NAME + "1", "测试消息2");
return "发送成功";
}
}
5.3、消费者
@Component
public class Consumer4 {
@RabbitListener(queues = {RabbitMQConfig4.COMMON_QUEUE_NAME})
public void receiveMsg1(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("普通队列接收到的消息内容:" + msg);
}
@RabbitListener(queues = {RabbitMQConfig4.BACKUP_QUEUE_NAME})
public void receiveMsg2(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println("备用队列接收到的消息内容:" + msg);
}
}
6、文件分片传输
6.1、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--引入与springboot集成的rabbitmq依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
6.2、消息对象
import java.io.Serializable;
public class Message implements Serializable {
// 分片文件
private byte[] body;
// 文件名
private String filename;
// 文件md5码
private String identifier;
// 文件uuid
private String uuid;
// 默认切片大小
private long chunkSize;
// 文件总大小
private long totalSize;
// 文件切片总数
private int totalChunks;
// 当前是第几片(切片)
private int chunkNumber;
// 当前切片大小
private long currentChunkSize;
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public long getChunkSize() {
return chunkSize;
}
public void setChunkSize(long chunkSize) {
this.chunkSize = chunkSize;
}
public long getTotalSize() {
return totalSize;
}
public void setTotalSize(long totalSize) {
this.totalSize = totalSize;
}
public int getTotalChunks() {
return totalChunks;
}
public void setTotalChunks(int totalChunks) {
this.totalChunks = totalChunks;
}
public int getChunkNumber() {
return chunkNumber;
}
public void setChunkNumber(int chunkNumber) {
this.chunkNumber = chunkNumber;
}
public long getCurrentChunkSize() {
return currentChunkSize;
}
public void setCurrentChunkSize(long currentChunkSize) {
this.currentChunkSize = currentChunkSize;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
}
6.3、生产者端临时文件对象
// 已接收文件信息
public class FileInfo {
FileInfo() {
}
// 已接收分片数量
private int number;
// 文件路径
private String filePath;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
6.4、RabbitMQ配置类
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RabbitMQ配置类
*
* @author 明快de玄米61
* @date 2022/5/1 18:49
*/
@Configuration
public class RabbitMQConfig1 {
// 队列名称
public static final String COMMON_QUEUE_NAME = "sharding_queue_springboot";
@Bean
public Queue commonQueue1() {
// 默认情况下,创建持久化、不排它、不自动删除的队列
return QueueBuilder.durable(COMMON_QUEUE_NAME).build();
}
}
6.5、生产者
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.crypto.digest.MD5;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.util.Arrays;
/**
* 生产者
*
* @author 明快de玄米61
* @date 2022/4/22 23:14
*/
@RestController
public class Provider1 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg61")
public String sendMsg() {
// 准备分片参数信息
// 待传输文件
File file = new File("C:\\Users\\mingming\\Downloads\\1云平台一站式部署.mp4");
// 文件UUID,组装文件分片的唯一标识
String uuid = UUID.fastUUID().toString();
// 文件名
String filename = file.getName();
// 文件md5码
String identifier = MD5.create().digestHex(file);
// 默认切片大小:1M
int chunkSize = 1024 * 1024;
// 文件总大小
int totalSize = (int) file.length();
// 文件切片总数
int totalChunks = totalSize % chunkSize == 0 ? totalSize / chunkSize : (totalSize / chunkSize + 1);
// 文件字节数组
byte[] fileContent = FileUtil.readBytes(file);
for (int i = 1; i <= totalChunks; i++) {
byte[] body;
if (i == totalChunks) {
// 最后一块
body = Arrays.copyOfRange(fileContent, chunkSize * (i - 1), totalSize);
} else {
body = Arrays.copyOfRange(fileContent, chunkSize * (i - 1), chunkSize * i);
}
// 当前是第几片(切片)
int chunkNumber = i;
// 当前切片大小
long currentChunkSize = body.length;
// 组装发送数据
Message message = new Message();
message.setBody(body);
message.setFilename(filename);
message.setIdentifier(identifier);
message.setUuid(uuid);
message.setChunkNumber(chunkNumber);
message.setChunkSize(chunkSize);
message.setCurrentChunkSize(currentChunkSize);
message.setTotalChunks(totalChunks);
message.setTotalSize(totalSize);
// 发送数据
rabbitTemplate.convertAndSend("", RabbitMQConfig1.COMMON_QUEUE_NAME, Convert.toPrimitiveByteArray(message));
}
return "发送成功";
}
}
6.6、消费者
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 消费者
*
* @author 明快de玄米61
* @date 2022/4/22 23:14
*/
@Component
public class Consumer1 {
// 文件信息存储Map
Map<String, FileInfo> map = new HashMap<>();
@RabbitListener(queues = {RabbitMQConfig1.COMMON_QUEUE_NAME})
public void receiveMsg(Message message, Channel channel) throws Exception {
com.atguigu.demo.file_sharding.Message msg = Convert.convert(com.atguigu.demo.file_sharding.Message.class, message.getBody());
String uuid = msg.getUuid();
// 处理首块分片
if (!map.containsKey(uuid)) {
String tmpdirPath = System.getProperty("java.io.tmpdir");
String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
String[] params = {tmpdirPath.substring(0, tmpdirPath.length() - 1), "tmpFile", dateStr, uuid};
String fileUrl = String.join(File.separator, Arrays.asList(params));
if (!new File(fileUrl).exists()) {
new File(fileUrl).mkdirs();
}
String filePath = fileUrl + File.separator + msg.getFilename();
new File(filePath).createNewFile();
FileInfo fileInfo = new FileInfo();
fileInfo.setNumber(0);
fileInfo.setFilePath(filePath);
map.put(uuid, fileInfo);
}
// 接收分片
FileInfo fileInfo = map.get(uuid);
//第一步 打开将要写入的文件
RandomAccessFile raf = new RandomAccessFile(fileInfo.getFilePath(), "rw");
//第二步 打开通道
try {
FileChannel fileChannel = raf.getChannel();
//第三步 计算偏移量
long position = (msg.getChunkNumber() - 1) * msg.getChunkSize();
//第四步 获取分片数据
byte[] fileData = msg.getBody();
//第五步 写入数据
fileChannel.position(position);
fileChannel.write(ByteBuffer.wrap(fileData));
fileChannel.force(true);
fileChannel.close();
fileInfo.setNumber(fileInfo.getNumber() + 1);
} catch (Exception e) {
e.printStackTrace();
} finally {
IoUtil.close(raf);
}
// 判断分片是否接收完成
if (msg.getTotalChunks() == map.get(uuid).getNumber()) {
// 文件接收完成
System.out.println("接收文件全路径:" + fileInfo.getFilePath());
// 移除map中文件信息
map.remove(uuid);
}
}
}
7、shovel绑定
7.1、windows中开启shovel插件
1) 在rabbitmq安装目录
下面的sbin
目录下打开DOS窗口
然后依次执行下面两条命令开启2个插件:
# 开启两个shovel插件
rabbitmq-plugins enable rabbitmq_shovel
# 一定也要开启这个,否则在RabbitMQ控制台无法看到shovel相关信息
rabbitmq-plugins enable rabbitmq_shovel_management
2) 登录RabbitMQ控制台就可以看到shovel相关信息了
7.2、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--引入与springboot集成的rabbitmq依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<!-- 发送shovel相关请求到RabbitMQ -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>http-client</artifactId>
<version>3.8.0.RELEASE</version>
</dependency>
7.3、配置文件
spring:
rabbitmq:
# 开启消息到交换机的发布确认
publisher-confirm-type: correlated
# 开启发布者回退,即消息无法投递到消费者,然后将消息回退给生产者
publisher-returns: true
# 主机;默认值:localhost
host: localhost
# 端口;默认值:5672
port: 5672
# 自定义RabbitMQ控制台访问端口,用于声明Client对象
console:
port: 15672
# 用户名;默认值:guest
username: guest
# 密码;默认值:guest
password: guest
# 虚拟主机;默认值:/
virtual-host: /
listener:
# 默认使用Simple类型,对应SimpleMessageListenerContainer类
simple:
# 能者多劳
prefetch: 1
7.4、RabbitMQ配置类
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RabbitMQ配置类
*
* @author 明快de玄米61
* @date 2022/4/28 13:16
*/
@Configuration
public class RabbitMQConfig7 {
// 交换机名称
public static final String COMMON_EXCHANGE_NAME = "common_exchange7";
// 起始队列名称
public static final String SOURCE_QUEUE_NAME = "source_queue";
// 绑定键
public static final String BINDING_KEY = "my_key";
// 路由键
public static final String ROUTING_KEY = "my_key";
// 目标队列名称
public static final String TARGET_QUEUE_NAME = "target_queue";
// shovel绑定关系名称
public static final String SHOVEL_NAME = "shovel_name";
@Autowired
private NoticeShovelListener noticeShovelListener;
@Bean
public SimpleMessageListenerContainer listenerShovelContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer listenerShovelContainer = new SimpleMessageListenerContainer();
listenerShovelContainer.setConnectionFactory(connectionFactory);
listenerShovelContainer.setBatchSize(10);
// 设置确认模式为手工确认
listenerShovelContainer.setAcknowledgeMode(AcknowledgeMode.MANUAL);
// 初始化时添加需要监听的队列名称
// listenerShovelContainer.setQueueNames(队列名称);
// 有需要监听的队列,后续还可以在添加
// listenerShovelContainer.addQueueNames(队列名称);
listenerShovelContainer.setMessageListener(noticeShovelListener);
listenerShovelContainer.start();
return listenerShovelContainer;
}
}
7.5、Shovel队列监听器类
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Component
public class NoticeShovelListener implements ChannelAwareMessageListener {
@Autowired
RabbitTemplate rabbitTemplate;
@Override
public void onMessage(Message message, Channel channel) throws IOException {
// 获取队列名称,可以通过判断队列名称来执行不同操作,这块根据实际业务需求来确定
String queueName = message.getMessageProperties().getConsumerQueue();
System.out.println("队列名称:" + queueName);
// 获取队列消息内容
System.out.println("消息内容:" + new String(message.getBody(), StandardCharsets.UTF_8));
// 手动确认消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
7.6、Shovel工具类
import com.rabbitmq.http.client.Client;
import com.rabbitmq.http.client.ClientParameters;
import com.rabbitmq.http.client.domain.ShovelDetails;
import com.rabbitmq.http.client.domain.ShovelInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* Shovel工具类
*
* @author 明快de玄米61
* @date 2023/8/21 23:05
*/
@Component
public class ShovelUtil {
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.console.port}")
private String consolePort;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
// 创建Shovel的Client对象
private static Client client;
@PostConstruct
public void init() {
// 初始化Client对象,用来发请求到rabbitmq
ClientParameters clientParameters;
try {
clientParameters = new ClientParameters()
.url("http://" + host + ":" + consolePort + "/api")
.username(username)
.password(password);
client = new Client(clientParameters);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取shovel所需rabbitmq连接Uri
**/
public static String getShovelUri(String ip, String port, String userName, String passWord) {
// 例如;amqp://guest:guest@127.0.0.1:5672
return "amqp://" + userName + ":" + passWord + "@" + ip + ":" + port;
}
/**
* 创建shovel队列+队列绑定关系
*
* @date 2023/8/21 23:38
* @param vhost 虚拟主机
* @param srcUri 起始连接信息,比如:amqp://guest:guest@127.0.0.1:5672
* @param targetUri 目标连接信息,比如:amqp://guest:guest@127.0.0.1:5672
* @param srcQueue 起始队列名称
* @param targetQueue 目标队列名称
* @param shovelName shovel绑定关系名称
**/
public static void createShovelQueue(String vhost, String srcUri, String targetUri, String srcQueue, String targetQueue, String shovelName) {
ShovelDetails shovelDetails = new ShovelDetails(srcUri, targetUri, 60L, false, null);
// 开启消息发布确认
shovelDetails.setAckMode("on-confirm");
shovelDetails.setSourceQueue(srcQueue);
shovelDetails.setDestinationQueue(targetQueue);
// shovel失效情况下,重新建立连接需要等待的秒数
shovelDetails.setReconnectDelay(60);
ShovelInfo shovelInfo = new ShovelInfo(shovelName, shovelDetails);
try {
client.declareShovel(vhost, shovelInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建shovel交换机+队列绑定关系
*
* @date 2023/8/21 23:38
* @param vhost 虚拟主机
* @param srcUri 起始连接信息,比如:amqp://guest:guest@127.0.0.1:5672
* @param targetUri 目标连接信息,比如:amqp://guest:guest@127.0.0.1:5672
* @param srcQueue 起始队列名称
* @param targetExchange 目标交换机名称
* @param shovelName shovel绑定关系名称
**/
public static void createShovelExchange(String vhost, String srcId, String targetId, String srcUri, String targetUri, String srcQueue, String targetExchange, String shovelName) {
ShovelDetails shovelDetails = new ShovelDetails(srcUri, targetUri, 60L, false, null);
// 开启消息发布确认
shovelDetails.setAckMode("on-confirm");
shovelDetails.setSourceQueue(srcQueue);
shovelDetails.setDestinationExchange(targetExchange);
ShovelInfo shovelInfo = new ShovelInfo(shovelName, shovelDetails);
try {
client.declareShovel(vhost, shovelInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取shovel列表
**/
public static List<ShovelInfo> getShovelList() {
return client.getShovels();
}
/**
* 删除shovel绑定关系
* @param shovelName shovel绑定关系名称
**/
public static void deleteShovel(String shovelName) {
client.deleteShovel("/", shovelName);
}
}
7.7、生产者
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 生产者
*
* @author 明快de玄米61
* @date 2022/4/28 13:13
*/
@RestController
public class Provider7 {
@Autowired
private AmqpAdmin amqpAdmin;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private ShovelUtil shovelUtil;
@Resource
private SimpleMessageListenerContainer listenerShovelContainer;
// 演示创建交换机、队列、队列和交换机绑定关系、Shovel绑定关系、Shovel监听器监听Shovel队列消息
@GetMapping("/init")
public String init() {
// 声明交换机
Exchange exchange = new DirectExchange(RabbitMQConfig7.COMMON_EXCHANGE_NAME, true, false, null);
amqpAdmin.declareExchange(exchange);
// 声明起始队列
Queue sourceQueue = new Queue(RabbitMQConfig7.SOURCE_QUEUE_NAME, true, false, false, null);
amqpAdmin.declareQueue(sourceQueue);
// 声明队列和交换机的绑定
Binding binding = new Binding(RabbitMQConfig7.SOURCE_QUEUE_NAME, Binding.DestinationType.QUEUE, RabbitMQConfig7.COMMON_EXCHANGE_NAME, RabbitMQConfig7.BINDING_KEY, null);
amqpAdmin.declareBinding(binding);
// 声明目标队列
Queue targetQueue = new Queue(RabbitMQConfig7.TARGET_QUEUE_NAME, true, false, false, null);
amqpAdmin.declareQueue(targetQueue);
// 拓展:既然可以声明队列和交换机,那就可以删除,由于当前场景不需要删除,所以代码注释掉了
// amqpAdmin.deleteExchange(交换机名称);
// amqpAdmin.deleteQueue(队列名称);
// 建立Shovel绑定关系
// 源头Rabbitmq连接信息
String sourceIp = "127.0.0.1";
String sourcePort = "5672";
String sourceUsername = "guest";
String sourcePassword = "guest";
// 目标Rabbitmq连接信息
String targetIp = "127.0.0.1";
String targetPort = "5672";
String targetUsername = "guest";
String targetPassword = "guest";
// 组装连接信息
String vhost = "/";
String sourceQueueName = RabbitMQConfig7.SOURCE_QUEUE_NAME;
String targetQueueName = RabbitMQConfig7.TARGET_QUEUE_NAME;
String shovelName = RabbitMQConfig7.SHOVEL_NAME;
String sourceUri = ShovelUtil.getShovelUri(sourceIp, sourcePort, sourceUsername, sourcePassword);
String targetUri = ShovelUtil.getShovelUri(targetIp, targetPort, targetUsername, targetPassword);
// 创建Shovel绑定关系
ShovelUtil.createShovelQueue(vhost, sourceUri, targetUri, sourceQueueName, targetQueueName, shovelName);
// Shovel监听器来监听队列消息并消费
listenerShovelContainer.addQueueNames(RabbitMQConfig7.TARGET_QUEUE_NAME);
return "成功";
}
@GetMapping("/sendMsg7")
public String sendMsg() {
// 发送消息到开始队列
rabbitTemplate.convertAndSend(RabbitMQConfig7.COMMON_EXCHANGE_NAME, RabbitMQConfig7.ROUTING_KEY, "你好,shovel!");
return "发送成功";
}
}
8、动态创建队列、交换机、绑定、消费数据
直接看shovel绑定
这一节
生产者
:详细介绍了如何创建队列
、交换机
、绑定
Shovel队列监听器类
和RabbitMQ配置类
:通过配置SimpleMessageListenerContainer来监听消息消费,并且设置Shovel队列监听器类
进行消息消费
9、完整版Demo
9.1、说明
完整版Demo中包含:
- 声明队列
- 声明交换机
- 声明队列和交换机的绑定关系
- 发送消息
- 消息出现问题退回
- 能者多劳
- 消息手动确定
- 以对象方式发送消息
- 以对象方式接收消息
9.2、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--引入与springboot集成的rabbitmq依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
9.3、配置文件
spring:
rabbitmq:
# 开启消息到交换机的发布确认
publisher-confirm-type: correlated
# 开启发布者回退,即消息无法投递到消费者,然后将消息回退给生产者
publisher-returns: true
# 主机;默认值:localhost
host: localhost
# 端口;默认值:5672
port: 5672
# 用户名;默认值:guest
username: guest
# 密码;默认值:guest
password: guest
# 虚拟主机;默认值:/
virtual-host: /
listener:
# 默认使用Simple类型,对应SimpleMessageListenerContainer类
simple:
# 能者多劳
prefetch: 1
# 全局设置手动确认消息在配置文件中配置;对于单个队列来说,可以通过@RabbitListener(XXX, ackMode = "MANUAL")设置;目前在注解中设置手动确认消息消费
# acknowledge-mode: manual
9.4、消息对象
import java.io.Serializable;
/**
* 学生类
*
* @author guoming
* @date 2023/8/27 12:33
*/
public class Student implements Serializable {
// 姓名
private String name;
// 年龄
private Integer age;
// 地址
private Address address;
public Student() {
}
public Student(String name, Integer age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
// 地址
static class Address implements Serializable {
// 国家
private String country;
// 省
private String province;
// 城市
private String city;
public Address() {
}
public Address(String country, String province, String city) {
this.country = country;
this.province = province;
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"country='" + country + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}
}
9.5、RabbitMQ配置类代码
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RabbitMQ配置类
*
* @author 明快de玄米61
* @date 2022/5/1 18:49
*/
@Configuration
public class RabbitMQConfig8 {
// 交换机名称
public static final String EXCHANGE_NAME = "full_version_exchange_springboot";
// 队列名称
public static final String QUEUE_NAME = "full_version_queue_springboot";
// 绑定键名称,指明队列和交换机的绑定关系
public static final String BINDING_KEY = "my_key";
// 路由键名称,对于直连交换机来说,只有 “绑定键” 全等于 “路由键”,才能把消息发到队列
public static final String ROUTING_KEY = "my_key";
@Bean
public Exchange commonExchange8() {
return ExchangeBuilder.directExchange(EXCHANGE_NAME).build();
}
@Bean
public Queue commonQueue8() {
return QueueBuilder.durable(QUEUE_NAME).build();
}
@Bean
public Binding commonBinding8(@Qualifier("commonQueue8") Queue commonQueue, @Qualifier("commonExchange8") Exchange commonExchange) {
return BindingBuilder.bind(commonQueue).to(commonExchange).with(BINDING_KEY).noargs();
}
}
9.6、生产者回调类
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
/**
* 发布确认
*
* @author 明快de玄米61
* @date 2022/4/30 18:12
*/
@Component
public class RabbitmqCallback implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
// rabbitTemplate被注入之后被调用,进行对象注入
@PostConstruct
public void init() {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnCallback(this);
}
// 无论消息发送到交换机成功或者失败,都会回调该方法,前提是在application.properties中配置:spring.rabbitmq.publisher-confirm-type=correlated
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
try {
String id = correlationData.getId();
System.out.printf("交换机接收消息反馈,其中id:%s,接收成功:%b,失败原因:%s\n", id, ack, cause);
} catch (Exception e) {
}
}
// 当交换机发送消息到队列失败的时候,将会回调该方法,成功不会回调,前提是在application.properties中配置:spring.rabbitmq.publisher-returns=true
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.printf("交换机发送消息到队列失败,其中消息内容:%s,回复码:%d,回复内容:%s,交换机:%s,路由:%s\n", new String(message.getBody(), StandardCharsets.UTF_8), replyCode, replyText, exchange, routingKey);
}
}
9.7、生产者
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 生产者
*
* @author 明快de玄米61
* @date 2022/4/22 23:14
*/
@RestController
public class Provider8 {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg8")
public String sendMsg() {
// 组装Student对象
Student.Address address = new Student.Address("中国", "湖北", "武汉");
Student student = new Student("明快de玄米61", 18, address);
// 发送消息
rabbitTemplate.convertAndSend(RabbitMQConfig8.EXCHANGE_NAME, RabbitMQConfig8.ROUTING_KEY, student);
return "发送成功";
}
}
9.8、消费者
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* 消费者
*
* @author 明快de玄米61
* @date 2022/4/22 23:14
*/
@Component
public class Consumer8 {
// 下面添加了一个ackMode 属性,并且配置为MANUAL,代表需要手动确定消息消费,相当于spring整合RabbitMQ时消费者中的channel.basicConsume()方法第2个参数设置为false,代表手动确认消息消费
@RabbitListener(queues = {RabbitMQConfig8.QUEUE_NAME}, ackMode = "MANUAL")
public void receiveMsg1(Message message, Channel channel, Student student) throws IOException {
// 数据接收方式1:字节数组
// String msg = new String(message.getBody(), StandardCharsets.UTF_8);
// 数据接收方式2:对象
System.out.println("消费者接收到的消息内容:" + student.toString());
// 手动确认消息;原因:@RabbitListener中设置了ackMode = "MANUAL"
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
10、说明
大多数用法说明已经写在了RabbitMQ学习文档(进阶篇(Demo使用Spring编写))中,这里主要说一下发布确认,以及发布确认和备用交换机的优先级次序,其中发布确认分为消息无法抵达交换机的发布确认,以及消息无法抵达队列的发布确认
(1)消息无法抵达交换机的发布确认
如果想开启消息无法抵达交换机的发布确认,那就需要在application.properties中配置:
spring.rabbitmq.publisher-confirm-type=correlated
然后让回调类实现RabbitTemplate.ConfirmCallback
,并且重写confirm
方法,其中confirm
方法中的代码如下:
// 无论消息发送到交换机成功或者失败,都会回调该方法,前提是在application.properties中配置:spring.rabbitmq.publisher-confirm-type=correlated
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
// 避免报错
try {
String id = correlationData.getId();
System.out.printf("交换机接收消息反馈,其中id:%s,接收成功:%b,失败原因:%s\n", id, ack, cause);
} catch (Exception e) {
}
}
其中在消息发送的时候需要添加CorrelationData
对象,其中上面的CorrelationData correlationData
就是我们在生产者发送消息的时候添加的该对象,生产者中添加CorrelationData
对象的代码如下:
CorrelationData correlationData1 = new CorrelationData();
correlationData1.setId("1");
rabbitTemplate.convertAndSend(RabbitMQConfig3.COMMON_EXCHANGE_NAME, RabbitMQConfig3.COMMON_QUEUE_NAME, "测试消息1", correlationData1);
(2)消息无法抵达队列的发布确认
如果想开启消息无法抵达队列的发布确认,那就需要在application.properties中配置:
spring.rabbitmq.publisher-returns=true
然后让回调类实现RabbitTemplate.ReturnCallback
,并且重写returnedMessage
方法,其中returnedMessage
方法中的代码如下:
// 当交换机发送消息到队列失败的时候,将会回调该方法,成功不会回调,前提是在application.properties中配置:spring.rabbitmq.publisher-returns=true
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.printf("交换机发送消息到队列失败,其中消息内容:%s,回复码:%d,回复内容:%s,交换机:%s,路由:%s\n", new String(message.getBody(), StandardCharsets.UTF_8), replyCode, replyText, exchange, routingKey);
}
(3)备用交换机
如果消息无法抵达队列的发布确认
和备用交换机
同时存在,然后消息无法从交换机发送到队列,那么备用交换机优先级更高,所以它会生效,那么消息无法抵达队列的发布确认
将不会生效