RabbitMQ学习文档(进阶篇(Demo使用SpringBoot编写))

一、依赖

<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)备用交换机

如果消息无法抵达队列的发布确认备用交换机同时存在,然后消息无法从交换机发送到队列,那么备用交换机优先级更高,所以它会生效,那么消息无法抵达队列的发布确认将不会生效

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot集成RabbitMQ可以通过以下步骤完成: 1. 添加Maven依赖:在pom.xml文件中添加RabbitMQ的Spring Boot Starter依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 2. 配置RabbitMQ连接信息:在application.properties(或application.yml)文件中配置RabbitMQ的连接信息。 ```properties spring.rabbitmq.host=your_rabbitmq_host spring.rabbitmq.port=your_rabbitmq_port spring.rabbitmq.username=your_rabbitmq_username spring.rabbitmq.password=your_rabbitmq_password ``` 3. 创建RabbitMQ发送者:创建一个发送消息的类,使用`RabbitTemplate`发送消息到指定的交换机和队列。 ```java import org.springframework.amqp.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class RabbitMQSender { @Autowired private RabbitTemplate rabbitTemplate; public void sendMessage(String exchange, String routingKey, Object message) { rabbitTemplate.convertAndSend(exchange, routingKey, message); } } ``` 4. 创建RabbitMQ接收者:创建一个接收消息的类,使用`@RabbitListener`注解监听指定的队列,处理接收到的消息。 ```java import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class RabbitMQReceiver { @RabbitListener(queues = "your_queue_name") public void receiveMessage(Object message) { // 处理接收到的消息 System.out.println("Received message: " + message.toString()); } } ``` 5. 发送和接收消息:在需要发送或接收消息的地方调用对应的方法。 ```java @Autowired private RabbitMQSender rabbitMQSender; public void sendMessage() { rabbitMQSender.sendMessage("your_exchange_name", "your_routing_key", "Hello, RabbitMQ!"); } ``` 以上是基本的使用方式,你可以根据实际需求行扩展和配置。注意,你还需要安装并启动RabbitMQ服务。 希望对你有所帮助!如果有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值