Spring Boot 中 RabbitMQ 的使用

news2025/3/11 9:21:23

目录

引入依赖

添加配置

Simple(简单模式)

生产者代码

消费者代码

​编辑

Work Queue(工作队列)

生产者代码

消费者代码

Publish/Subscribe(发布/订阅)

生产者代码

消费者代码

Routing(路由模式)

生产者代码

消费者代码

Topics(通配符模式)

生产者代码

消费者代码

常见问题

交换机与队列的绑定

交换机类型错误

队列属性错误


在 RabbitMQ 的工作模式_发布订阅和工作模式的区别-CSDN博客 中,我们学习了 RabbitMQ 的 7 种工作模式,接下来,在本篇文章中,我们就来在 Spring Boot 中实现常见的工作模式(由于 RPC 模式 和 发布确认模式 使用较少,因此在这里就不进行介绍了),进而学习在 Spring Boot 中如何使用 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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

或是在创建项目时,添加依赖:

接着,需要添加配置

添加配置

spring:
  rabbitmq:
    host: 49.232.238.62
    port: 5672 #默认为5672
    username: admin
    password: 123456
    virtual-host: test02

或是:

spring:
    rabbitmq:
      addresses: amqp://admin:123456@49.232.238.62:5672/test02

在这里,使用的虚拟机为 test02,因此,需要创建虚拟机 test02:

创建成功: 

接下来,就可以开始编写代码了

Simple(简单模式)

在简单模式下,只有一个生产者和一个消费者,生产者生产的消息存储到队列后,由这个消费者消费

我们先来实现生产者代码

生产者代码

为了方便进行测试,我们通过 接口 来发送消息

创建 Constants 类,定义 简单模式 下使用的队列名称:

public class Constants {
    // 简单模式
    public static final String SIMPLE_QUEUE = "simple.queue";
}

接着,需要声明队列

创建 RabbitMQConfig 类,创建 简单模式 下使用的队列:

注意不要导错包了,当前我们使用的队列位于 org.springframework.amqp.core 包下 

使用 QueueBuilder 声明队列:

import com.example.springrabbitmq.constant.Constants;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {
    // 简单模式
    @Bean("simpleQueue")
    public Queue simpleQueue() {
        return QueueBuilder.durable(Constants.SIMPLE_QUEUE).build();
    }
}

由于声明的队列来自于第三方的包,且后续 工作模式、发布定义模式等使用的队列也在 RabbitMQConfig 中定义,因此需要使用 @Bean 注解

其中 durable 表示 持久化,调用 durable 方法,表示创建一个持久化队列

接着,调用 build 方法创建 Bean

不要忘记添加 @Configuration 注解

在这里,我们直接使用 内置的交换机 来进行路由,因此,不需要声明交换机 以及 绑定交换机和队列

接着,我们创建 ProducerController 类,实现 简单模式 下的生产者:

在 Spring 中,使用 RabbitTemplate 来操作 RabbitMQ,因此,我们将它注入进来:

@RestController
@RequestMapping("/producer")
public class ProducerController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
}

接着,使用 convertAndSend 方法来发送消息:

public void convertAndSend(String exchange, String routingKey, final Object object) throws AmqpException

exchange:交换机名称,使用内置的交换机时,为 ""

routingKey:路由键,用内置交换机时,routingKey 为队列名称

object:要发送的消息

    @RequestMapping("simple")
    public String simple() {
        for (int i = 0; i < 20; i++) {
            String message = "simple... " + i;
            rabbitTemplate.convertAndSend("", Constants.SIMPLE_QUEUE, message);
        }
        return "OK";
    }
    

完整代码:

import com.example.springrabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/producer")
public class ProducerController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @RequestMapping("simple")
    public String simple() {
        for (int i = 0; i < 20; i++) {
            String message = "simple... " + i;
            rabbitTemplate.convertAndSend("", Constants.SIMPLE_QUEUE, message);
        }
        return "OK";
    }
}

 运行代码,观察 Queues and Streams

此时并未创建队列 

访问 127.0.0.1:8080/producer/simple,再次观察:

simple.queue 队列被创建,且队列中已有 20 条消息,也就是说,当我们运行程序时,队列并不会立即创建,而是当我们访问接口,要向队列中发送消息时,才会创建队列

我们查看其中一条消息:

消息正确发送

接下来,我们继续实现消费者代码

消费者代码

消费者需要 监听队列,当队列中有消息时,从队列中获取消息并消费,因此,我们创建监听类 SimpleListener 

public class SimpleListener {
    
}

接下来,如何监听队列中的消息呢?

 Spring 为我们提供了 @RabbitListener 注解,用于监听 RabbitMQ 队列,通过这个注解,我们可以定义一个方法,以便从队列中接收消息

@RabbitListener 支持多种参数类型,这些参数类型代表了从队列接收到的消息和相关信息

常见参数类型:

String:消息内容

Message(org.springframework.amqp.core.Message):Spring AMQP 的 Message 类,返回原始的消息体以及消息属性(如:消息ID、内容、队列信息等)

Channel(com.rabbitmq.client.Channel):RabbitMQ 的通道对象,可以用于进行更高级的操作,如手动确认消息

接下来,我们就来实现消费者监听队列: 

 使用  @RabbitListener 时,需要指定监听的队列

import com.example.springrabbitmq.constant.Constants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class SimpleListener {
    @RabbitListener(queues = Constants.SIMPLE_QUEUE)
    public void simpleListener(Message message) {
        System.out.println("listener 从队列 " + Constants.SIMPLE_QUEUE +" 中接收到消息:" + message);
    }
}

不要忘记添加 @Component,将其交给 Spring 管理

我们再次运行代码,观察打印的日志:

队列中的 20 条消息瞬间就被消费者消费掉了

可以看到,Message 中包含了 交换机、RoutingKey、deliverTage、监听的队列等信息

且观察 Connection Channel

此时有一个 Connection

两个 Channel

哪一个是消费者的哪一个是生产者的呢? 

 我们可以从 Message rates 中确定,发布消息的是生产者,消费消息的是消费者

也可以根据 consumer 确定:

而当有多个消费者时,可以通过 ConsumerTag 来确定不同的消费者:

接下来,我们继续学习工作队列模式

Work Queue(工作队列)

工作队列模式下,有一个生产者和多个消费者多个消费者共同消费消息

 首先在 Constants 中声明工作队列模式下使用的队列:

    // 工作模式
    public static final String WORK_QUEUE = "work.queue";

RabbitMQConfig 中声明队列:

    // 工作队列模式
    @Bean("workQueue")
    public Queue workQueue() {
        return QueueBuilder.durable(Constants.WORK_QUEUE).build();
    }

我们仍然使用内置的交换机进行路由,因此,也就不需要声明交换机和绑定交换机和队列

接下来, 我们就来实现生产者

生产者代码

工作队列模式与简单模式的区别在于工作模式下有多个消费者,因此生产者的消费代码与简单模式相同

ProducerController 中发送消息:

    @RequestMapping("/work")
    public String work() {
        for (int i = 0; i < 20; i++) {
            String message = "work... " + i;
            rabbitTemplate.convertAndSend("", Constants.WORK_QUEUE, message);
        }
        return "OK";
    }

运行,并访问 http://127.0.0.1:8080/producer/work,观察结果:

 

成功创建队列,且 20 条消息成功发送

接着,我们继续实现消费者代码

消费者代码

消费者的代码也与简单模式下的代码相同,只是在这里我们需要创建两个消费者:

我们可以定义 WorkListener1  WorkListener2,分别定义一个方法来监听:

import com.example.springrabbitmq.constant.Constants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class WorkListener1 {
    @RabbitListener(queues = Constants.WORK_QUEUE)
    public void queueListener1(Message message) {
        System.out.println("listener1 从队列 " + Constants.SIMPLE_QUEUE +" 中接收到消息:" + message);
    }
}

import com.example.springrabbitmq.constant.Constants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class WorkListener2 {
    @RabbitListener(queues = Constants.WORK_QUEUE)
    public void queueListener2(Message message) {
        System.out.println("listener2 从队列 " + Constants.SIMPLE_QUEUE +" 中接收到消息:" + message);
    }
}

再次运行,观察结果:

可以看到两个消费者共同消费了这 20 条消息,且它们的 deliveryTag 都是从 1 开始计数的

接下来,我们继续学习 发布定义模式

Publish/Subscribe(发布/订阅)

在发布/订阅模式中,每个消费者收到的消息都是相同的,且多了 Exchange 角色

在前面 简单模式 和 工作队列模式 下,图中都没有出现交换机,但实际上,生产者生产的消息都是先发送到交换机,然后再路由到队列中的。在前两种模式下,直接使用 RabbitMQ 提供的内置交换机就可以实现,因此,并没有突出交换机的存在,但实际上生产者生产的消息不会直接投递到队列中

因此,在 发布/订阅 模式下,我们就需要声明交换机,并绑定队列和交换机

先在 Constants 类中声明 发布订阅模式 下使用的交换机和队列:

    // 发布订阅模式
    public static final String PUBLISH_CHANGE = "fanout";
    public static final String PUBLISH_QUEUE_1 = "publish.queue.1";
    public static final String PUBLISH_QUEUE_2 = "publish.queue.2";

 接着,在 RabbitMQConfig 中声明队列:

    // 发布订阅模式
    @Bean("fanoutQueue1")
    public Queue fanoutQueue1() {
        return QueueBuilder.durable(Constants.PUBLISH_QUEUE_1).build();
    }

    @Bean("fanoutQueue2")
    public Queue fanoutQueue2() {
        return QueueBuilder.durable(Constants.PUBLISH_QUEUE_2).build();
    }

还需要声明交换机

在 发布订阅模式 下,交换机的类型为 Fanout(广播)

在 Spring 中,使用 FanoutExchange 表示广播类型的交换机

    // 声明交换机
    @Bean("fanoutExchange")
    public FanoutExchange fanoutExchange() {
        return ExchangeBuilder.fanoutExchange(Constants.PUBLISH_CHANGE).durable(true).build();
    }

使用 ExchangeBuilder 的 fanoutExchange 方法创建广播类型的交换机

接着,需要将队列和交换机进行绑定

在 Spring 中,使用 Binding(org.springframework.amqp.core.Binding)表示交换机与队列的绑定关系

使用 BindingBuilder 进行绑定:

但此时队列不能自动注入, 因为此时有多个 Queue 类型的队列

我们可以使用 @Qualifier 注解指定我们要绑定的队列(fanoutExchange 由于只有一个,可以不指定):

    // 绑定交换机和队列
    @Bean("fanoutQueueBinding1")
    public Binding fanoutQueueBinding1(@Qualifier("fanoutExchange") FanoutExchange fanoutExchange,@Qualifier("fanoutQueue1") Queue queue) {
        return BindingBuilder.bind(queue).to(fanoutExchange);
    }

    @Bean("fanoutQueueBinding2")
    public Binding fanoutQueueBinding2(@Qualifier("fanoutExchange") FanoutExchange fanoutExchange, @Qualifier("fanoutQueue2") Queue queue) {
        return BindingBuilder.bind(queue).to(fanoutExchange);
    }

 接着,我们就可以使用接口发送消息了

生产者代码

在 ProducerController 中使用 rabbitTemplate 发送消息

    @RequestMapping("/fanout")
    public String fanout() {
        for (int i = 0; i < 20; i++) {
            String message = "fanout ... " + i;
            rabbitTemplate.convertAndSend(Constants.PUBLISH_CHANGE, "", message);
        }
        return "OK";
    }

注意,发布订阅模式下,routingKey 为空,表示所有队列都可以收到消息

运行程序,访问 http://127.0.0.1:8080/producer/fanout,并观察结果

两个队列中都有 20 条消息,消费发送成功

接着,我们继续编写消费者代码

消费者代码

发布订阅模式下,消费者的代码与工作模式下的代码相同

为了方便观察,在这里就直接将两个消费者写在一个类中:

@Component
public class FanoutListener {
    @RabbitListener(queues = Constants.PUBLISH_QUEUE_1)
    public void queueListener1(String message) {
        System.out.println("listener1 从队列 " + Constants.PUBLISH_QUEUE_1 +" 中接收到消息:" + message);
    }

    @RabbitListener(queues = Constants.PUBLISH_QUEUE_2)
    public void queueListener2(String message) {
        System.out.println("listener2 从队列 " + Constants.PUBLISH_QUEUE_2 +" 中接收到消息:" + message);
    }
}

由于我们只关心消息的内容,因此,可以只使用 String 类型来接收消息

再次运行,观察打印的日志:

两个消费者都消费了 20 条相同的消息

我们继续看路由模式

Routing(路由模式)

Routing 模式下,队列与交换机之间的绑定,不再是任意的绑定了,而是需要指定一个 BindingKey

生产者在向 交换机 发送消息时,也需要指定消息的 RoutingKey

交换机不会将消息发送给每一个绑定的 key,而是会根据消息的 RoutingKey 进行判断,只有队列绑定时的 BindingKey 和发送消息的 RoutingKey 完全一致时,才会接收消息

先在 Constants 中声明路由模式下使用的队列和交换机:

    // 路由模式
    public static final String ROUTING_CHANGE = "routing";
    public static final String ROUTINT_QUEUE_1 = "routing.queue.1";
    public static final String ROUTINT_QUEUE_2 = "routing.queue.2";

接着,在 RabbitMQConfig 中声明队列:

    // 路由模式
    @Bean("routingQueue1")
    public Queue routingQueue1() {
        return QueueBuilder.durable(Constants.ROUTINT_QUEUE_1).build();
    }

    @Bean("routingQueue2")
    public Queue routingQueue2() {
        return QueueBuilder.durable(Constants.ROUTINT_QUEUE_2).build();
    }

声明交换机:

注意,路由模式下,交换机类型为 Direct(定向)

在 Spring 中,使用 DirectExchange表示广播类型的交换机

    // 声明交换机
    @Bean("routingExchange")
    public DirectExchange routingExchange() {
        return ExchangeBuilder.directExchange(Constants.ROUTING_CHANGE).durable(true).build();
    }

使用 ExchangeBuilder 的 directExchange 方法创建通配符类型的交换机

绑定队列和交换机:

    // 绑定交换机和队列
    @Bean("routingQueueBinding1")
    public Binding routingQueueBinding1(@Qualifier("routingExchange") DirectExchange directExchange, @Qualifier("routingQueue1") Queue queue) {
        return BindingBuilder.bind(queue).to(directExchange).with("a");
    }

    @Bean("routingQueueBinding2")
    public Binding routingQueueBinding2(@Qualifier("routingExchange") DirectExchange directExchange, @Qualifier("routingQueue2") Queue queue) {
        return BindingBuilder.bind(queue).to(directExchange).with("a");
    }

    @Bean("routingQueueBinding3")
    public Binding routingQueueBinding3(@Qualifier("routingExchange") DirectExchange directExchange, @Qualifier("routingQueue2") Queue queue) {
        return BindingBuilder.bind(queue).to(directExchange).with("b");
    }

    @Bean("routingQueueBinding4")
    public Binding routingQueueBinding4(@Qualifier("routingExchange") DirectExchange directExchange, @Qualifier("routingQueue2") Queue queue) {
        return BindingBuilder.bind(queue).to(directExchange).with("c");
    }

在绑定交换机时,需要调用 with 方法指定 BindingKey

 之后,就可以发送消息了

生产者代码

ProducerController 中使用 rabbitTemplate 发送消息

在 路由模式 下,需要指定 RoutingKey,为了测试不同 RoutingKey 发送消息的情况,我们在路径中指定 RoutingKey

    @RequestMapping("/routing/{routingKey}")
    public String direct(@PathVariable("routingKey") String routingKey) {
        rabbitTemplate.convertAndSend(Constants.ROUTING_CHANGE, routingKey, "routing test... " +routingKey);
        return "OK";
    }

运行程序,访问 http://127.0.0.1:8080/producer/routing/a 、http://127.0.0.1:8080/producer/routing/b 和 http://127.0.0.1:8080/producer/routing/c

观察结果:

routing.queue.1 中只有 1 条消息,routing.queue.2 中有 3 条消息

只有当 routingKey 为 a 时,才能与 routing.queue.1 的 BindingKey 匹配,而 routingKey 为 a、b、c 时,都能与 routing.queue.2 的 BindingKey 相匹配,因此routing.queue.1 中只有 1 条消息,routing.queue.2 中有 3 条消息

我们查看 routing.queue.2 中的消息:

接着,我们继续实现消费者代码

消费者代码

消费者的代码与 发布订阅模式 的相同,因此我们只需要修改消费的队列即可:

@Component
public class RoutingListener {
    @RabbitListener(queues = Constants.ROUTINT_QUEUE_1)
    public void queueListener1(String message) {
        System.out.println("listener1 从队列 " + Constants.ROUTINT_QUEUE_1 +" 中接收到消息:" + message);
    }

    @RabbitListener(queues = Constants.ROUTINT_QUEUE_2)
    public void queueListener2(String message) {
        System.out.println("listener2 从队列 " + Constants.ROUTINT_QUEUE_2 +" 中接收到消息:" + message);
    }
}

再次运行,观察日志:

我们继续看 通配符模式

Topics(通配符模式)

相比于 routing 模式,topics 类型的交换机在匹配规则上进行了扩展,BindingKey 支持通配符匹配

RoutingKey 是一系列由 . 分割的单词,如 user.name、work.abc等

BindingKey 也和 RoutingKey 一样,由 . 分割的字符串

BindingKey 中可以存在两种特殊的字符串,用于模糊匹配:

* :表示能够匹配任意一个单词

#:表示能够匹配任意多个单词(可以为 0 个)

 先在 Constants 中声明通配符模式下使用的队列和交换机:

    // 通配符模式
    public static final String TOPICS_CHANGE = "topics";
    public static final String TOPICS_QUEUE_1 = "topics.queue.1";
    public static final String TOPICS_QUEUE_2 = "topics.queue.2";

接着,在 RabbitMQConfig 中声明队列:

    // 通配符模式
    @Bean("topicsQueue1")
    public Queue topicsQueue1() {
        return QueueBuilder.durable(Constants.TOPICS_QUEUE_1).build();
    }
    @Bean("topicsQueue2")
    public Queue topicsQueue2() {
        return QueueBuilder.durable(Constants.TOPICS_QUEUE_2).build();
    }

声明交换机:

注意,通配符模式下,交换机类型为 Topics(通配符)

在 Spring 中,使用 TopicExchange 表示通配符类型的交换机

    @Bean("topicsExchange")
    public TopicExchange topicExchange() {
        return ExchangeBuilder.topicExchange(Constants.TOPICS_CHANGE).durable(true).build();
    }

使用 ExchangeBuilder 的 topicExchange 方法创建通配符类型的交换机

绑定队列和交换机:

    // 绑定交换机和队列
    @Bean("topicsQueueBinding1")
    public Binding topicsQueueBinding1(@Qualifier("topicsExchange") TopicExchange topicExchange, @Qualifier("topicsQueue1") Queue queue) {
        return BindingBuilder.bind(queue).to(topicExchange).with("*.*.a");
    }

    @Bean("topicsQueueBinding2")
    public Binding topicsQueueBinding2(@Qualifier("topicsExchange") TopicExchange topicExchange, @Qualifier("topicsQueue2") Queue queue) {
        return BindingBuilder.bind(queue).to(topicExchange).with("*.b.*");
    }
    @Bean("topicsQueueBinding3")
    public Binding topicsQueueBinding3(@Qualifier("topicsExchange") TopicExchange topicExchange, @Qualifier("topicsQueue2") Queue queue) {
        return BindingBuilder.bind(queue).to(topicExchange).with("c.#");
    }

调用 with 方法指定 BindingKey

接下来,就可以发送消息了

生产者代码

ProducerController 中使用 rabbitTemplate 发送消息

同样的,在 通配符模式 下,需要指定 RoutingKey,为了测试不同 RoutingKey 发送消息的情况,我们在路径中指定 RoutingKey

    @RequestMapping("/topics/{topicsKey}")
    public String topics(@PathVariable("topicsKey") String topicsKey) {
        rabbitTemplate.convertAndSend(Constants.TOPICS_CHANGE, topicsKey, "topics test... " +topicsKey);
        return "OK";
    }

运行程序,并访问 http://127.0.0.1:8080/producer/topics/a.b.a 、http://127.0.0.1:8080/producer/topics/c.work 和 http://127.0.0.1:8080/producer/topics/a.a.a

观察结果:

topics.queue.1 和  topics.queue.2 中都有两条消息,我们先来看 topics.queue.1:

 topics.queue.2 中的消息:

当 topicsKey为 a.b.a 时,能与 topics.queue.1 的 BindingKey(*.*.a) 匹配,也能与 topics.queue.2 的 BindingKey(*.b.*) 匹配,因此,消息会被路由到两个队列中

topicsKey 为 c.work 时,只能与 topics.queue.2 的 BindingKey(c.#) 相匹配

topicsKey 为 a.a.a 时,只能与 topics.queue.1 的 BindingKey(*.*.a) 相匹配

我们继续实现消费者代码

消费者代码

消费者的代码与 路由模式 的相同,因此我们只需要修改消费的队列即可:

@Component
public class TopicsListener {
    @RabbitListener(queues = Constants.TOPICS_QUEUE_1)
    public void queueListener1(String message) {
        System.out.println("listener1 [" + Constants.TOPICS_QUEUE_1 +"] 接收到消息: " + message);
    }

    @RabbitListener(queues = Constants.TOPICS_QUEUE_2)
    public void queueListener2(String message) {
        System.out.println("listener2 [" + Constants.TOPICS_QUEUE_2 +"] 接收到消息: " + message);
    }
}

再次运行,观察日志:

常见问题

在我们编写代码的过程中,可能会出现一些问题,接下来,我们就一起来看看常见的错误类型

交换机与队列的绑定

例如,在 通配符模式 下,交换机类型绑定为 Direct(定向)模式:

此时,若使用了 @Qualifier 注解,则会直接报错,显示类型不匹配

但是,若未使用 @Qualifier 注解,则不会报错,程序也能正常运行

我们观察交换机 routing:

可以看到 topics.queue.1 与 routing 交换机进行了绑定,我们再访问 , 消息成功发送

但 topics.queue.1 队列中始终没有消息,这是因为 topics.queue.1 此时并未与 topics 交换机进行绑定,topics 交换机在接收到消息后,发现没有匹配的 BindingKey,就直接将消息丢弃了

当消息发送成功,但队列中却没有消息时,就需要检查队列和交换机的绑定关系了

交换机类型错误

在我们定义交换机时,可能会一不小心将交换机的名称写错:

我们运行程序,观察日志:

 我们来看 ERROR 信息显示在 虚拟机 test02 上的交换机 fanout 接收到的类型为 topic,但当前 fanout 是 fanout 类型的

fanout交换机 是 fanout 类型的,但是此时将其当做 topic 类型来使用,也就报错了

一个交换机已经存在,则不能再修改其类型了,若我们需要进行修改,则需要将之前已经存在的交换机删除

队列属性错误

例如,topics.queue.1 是一个持久化的队列,但此时我们将其声明为 非持久化 的:

 再次运行:

也显示了 ERROR 信息,提示 在虚拟机 test02 上的队列 topics.queue.1 是持久化的,接收到的参数是 false,但当前为 true

也就是说,我们尝试修改队列的持久化参数,此时是不被允许的

topics.queue.1 是持久化的:

D 表示 durable 为 true 

若我们需要将其修改为非持久化的,需要先将已经存在的  topics.queue.1 队列删除:

此时再次运行程序,访问http://127.0.0.1:8080/producer/topics/a.a.a

 观察结果:

 此时,topics.queue.1 就是非持久化的了

当交换机、队列创建完成时,其属性是不能发生变化的,若需要修改,则需要将当前交换机或队列删除,然后重新声明

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【linux基础I/O(1)】文件描述符的本质重定向的本质

目录 前言1. 理解C语言的文件接口2. 操作文件的系统调用接口2.1 open函数详解2.2 close函数详解2.3 write函数详解2.4 read函数详解 3. 文件描述符fd详解4. 文件描述符的内核本质5. 怎样理解Linux下一切皆文件?6. 理解输出输入重定向7. 重定向的系统调用8. 总结 前言 “在Lin…

全面解析 Node-RED:功能、Docker 部署与实战示例

言简意赅的讲解Node-RED解决的痛点 Node-RED 是一个基于流的编程工具&#xff0c;专为物联网&#xff08;IoT&#xff09;应用而设计。它通过可视化的编程界面&#xff0c;使开发者能够轻松地连接各种硬件设备、API 以及在线服务&#xff0c;构建复杂的应用流程。本文将详细介…

2、pycharm常用快捷命令和配置【持续更新中】

1、常用快捷命令 Ctrl / 行注释/取消行注释 Ctrl Alt L 代码格式化 Ctrl Alt I 自动缩进 Tab / Shift Tab 缩进、不缩进当前行 Ctrl N 跳转到类 Ctrl 鼠标点击方法 可以跳转到方法所在的类 2、使用pip命令安装request库 命令&#xff1a;pip install requests 安装好了…

2025-01-04 Unity插件 YodaSheet1 —— 插件介绍

文章目录 1 介绍2 工作原理2.1 ScriptableObject -> YadeSheetData2.2 YadeDatabase 存储多个 YadeSheetData 3 用途4 缺点5 推荐 1 介绍 ​ Yade 提供类似于 Excel 或者 Google Sheets 的表格编辑器&#xff0c;可以轻松地在 Unity 编辑器中 编辑&#xff0c;搜索&#xf…

用 C++ 创建控制台计算器

本文内容 先决条件创建应用项目验证新应用是否生成并运行编辑代码 显示另外 5 个 C 程序员通常从在命令行上运行的“Hello, world!”应用程序开始。 你将以本文为起点&#xff0c;逐步进阶&#xff0c;加深学习难度&#xff1a;计算器应用。 先决条件 在 Visual Studio 中…

IDEA 撤销 merge 操作(详解)

作为一个开发者&#xff0c;我们都知道Git是一个非常重要的版本控制工具&#xff0c;尤其是在协作开发的过程中。然而&#xff0c;在使用Git的过程中难免会踩一些坑&#xff0c;今天我来给大家分享一个我曾经遇到的问题&#xff1a;在使用IDEA中进行merge操作后如何撤销错误的合…

限时特惠,香港服务器,低至53元/年

家人们谁懂啊&#xff01;香港服务器这价格简直逆天了&#xff0c;居然比内地的还便宜&#xff01;就拿阿里云来说&#xff0c;人家最低配置的服务器&#xff0c;价格都很难做到这么亲民。 最低配的就不说了&#xff0c;2 核 4G 的配置&#xff0c;应对日常业务稳稳当当&#x…

EF Core配置及使用

Entity Framework Core是微软官方的ORM框架。 ORM&#xff1a;Object Relational Mapping。让开发者用对象操作的形式操作关系数据库。 EF Core是对于底层ADO.NET Core的封装&#xff0c;因此ADO.NET Core支持的数据库不一定被EF Core支持。 代码创建数据库Code First 建实…

GPT分区 使用parted标准分区划分,以及相邻分区扩容

parted 是一个功能强大的命令行工具&#xff0c;用于创建和管理磁盘分区表和分区。它支持多种分区表类型&#xff0c;如 MBR&#xff08;msdos&#xff09;、GPT&#xff08;GUID Partition Table&#xff09;等&#xff0c;并且可以处理大容量磁盘。parted 提供了一个交互式界…

【mybatis-plus问题集锦系列】使用mybatis实现数据的基础增删改查

使用mybatis实现数据的基础增删改查,简单的增删改查操作方法步骤 代码实现 pom.xml <dependencies><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.…

tlias项目实战笔记

一个小项目写了一个多月&#xff0c;在考试周穿插&#xff0c;终于能有时间来写个小总结了&#xff0c;废话少说&#xff0c;我们直接来步入正题。 一、项目开发规范 1.开发风格Restful 案例是基于当前最为主流的前后端分离模式进行开发。 在前后端分离的开发模式中&#xff…

Arduino Uno简介与使用方法

目录 一、Arduino Uno概述 1. 硬件特性 2. 开发环境 二、Arduino Uno的基本使用方法 1. 硬件连接 2. 软件编程 三、Arduino Uno编程基础 1. 基本语法 2. 常用函数 四、Arduino Uno应用举例 1. LED闪烁 2. 温度检测 3. 超声波测距 五、Arduino Uno的扩展与应用 1…

go 模拟TCP粘包和拆包,及解决方法

1. 什么是 TCP 粘包与拆包&#xff1f; 粘包&#xff08;Sticky Packet&#xff09; 粘包是指在发送多个小的数据包时&#xff0c;接收端会将这些数据包合并成一个数据包接收。由于 TCP 是面向流的协议&#xff0c;它并不会在每次数据发送时附加边界信息。所以当多个数据包按顺…

新能源电动汽车动力电池技术

新能源电动汽车动力电池技术是新能源汽车发展的核心之一&#xff0c;以下是动力电池技术的一些关键方面&#xff1a; 技术进展 能量密度提升&#xff1a;近年来&#xff0c;动力电池的能量密度有了显著提升&#xff0c;从2010年的100Wh/kg提高到2024年的300Wh/kg。能量密度的…

仓颉笔记——windows11安装启用cangjie语言,并使用vscode编写“你好,世界”

2025年1月1日第一篇日记&#xff0c;大家新年好。 去年就大致看了一下&#xff0c;感觉还不错&#xff0c;但一直没上手&#xff0c;这次借着元旦的晚上安装了一下&#xff0c;今年正式开动&#xff0c;公司众多的应用国产化正等着~~ 第一步&#xff1a;准备 官网&#xff1a;…

JVM对象内存结构

1对象内存结构说明 注意&#xff1a; 如果对象为数组对象&#xff0c;在对象头后面有4字节存储数组长度&#xff1b; 1.1对象头 对象头分为Mark Word和Class Pointer两部分&#xff1b; Mark Word&#xff1a;对象基础信息 32位操作系统中占4字节&#xff0c;64位操作系统中占8…

doris:基于 Arrow Flight SQL 的高速数据传输链路

Doris 基于 Arrow Flight SQL 协议实现了高速数据链路&#xff0c;支持多种语言使用 SQL 从 Doris 高速读取大批量数据。 用途​ 从 Doris 加载大批量数据到其他组件&#xff0c;如 Python/Java/Spark/Flink&#xff0c;可以使用基于 Arrow Flight SQL 的 ADBC/JDBC 替代过去…

算法题(25):只出现一次的数字(三)

审题&#xff1a; 该题中有两个元素只出现一次并且其他元素都出现两次&#xff0c;需要返回这两个只出现一次的数&#xff0c;并且不要求返回顺序 思路: 由于对空间复杂度有要求&#xff0c;我们这里不考虑哈希表。我们采用位运算的方法解题 方法&#xff1a;位运算 首先&#…

HTML——75. 内联框架

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>内联框架</title><style type"text/css">iframe{width: 100%;height: 500px;}</style></head><body><!--iframe元素会创建包含…

MotionCtrl: A Unified and Flexible Motion Controller for Video Generation 论文解读

目录 一、概述 二、相关工作 三、前置知识 1、LVDM Introduction 2、LVDM Method 3、LVDM for Short Video Generation 4、Hierarchical LVDM for Long Video Generation 5、训练细节 6、推理过程 四、MotionCtrl 1、CMCM 2、OMCM 3、训练策略 五、实验 一、概述…