RabbitMQ的高级特性

消息确认机制

当消息发送成功后,到达消费者,通常会有两种情况:1.消息处理成功 2.消息处理异常

为了保证消息从队列成功的到达了消费者,并被消费者消费,RabbieMQ提供了消息确认机制

消息确认的机制分为两种

1.自动确认:当autoAck为true时,Rabbitmq会自动的发送出去的消息设置为true,无论消费者是否真正的消费了消息,一般应用于对消息可靠性不高的场景

2.手动确认:当autoAck为false时,Rabbitmq会等待消费者调用BasicAck命令,回复确认后才从内存中进行删除,这种模式一般应用于对消息可靠性较高的场景

手动确认消息的方法

肯定确认

第一个参数是deliveryTag,是用来标记消息,作为消息的唯一标识

第二个参数是multiple,是否批量的确认

java 复制代码
 channel.basicAck(getDeliveryTag,false);

否定确认

第一个参数是deliveryTag,和肯定确认中的deliveryTag参数作用一样

第二个参数是requeue,表示拒绝后,这条消息如何处理,如果该参数设为true,则表示会重新将参数存入队列,就行发送给下一个订阅该消息的消费者,如果设置为false,则RabbitMQ会把消息从队列中移除,而不会把他发送给新的消费者。

Spring AMQP对消息确认提供了三种策略

java 复制代码
        acknowledge-mode: auto #自动确认模式
        acknowledge-mode: manual #手动确认模式
        acknowledge-mode: none  #无确认模式

NONE:无确认模式

rabbitmq只要收到消息就认为已经消费,不管listener是否成功处理

AUTO:自动确认模式

Spring 自动 basicAck ;如果抛异常则 basicReject

MANUAL:手动确认模式

需要自己在代码中调用channel.basicAck()或basicNack()或者basicReject()

手动确认模式(MANUAL)

创建队列和交换机

java 复制代码
@Configuration
public class RabbitConfig {
//    消息确认

//    创建队列
    @Bean("ackQueue")
    public Queue ackQueue(){
        return QueueBuilder.durable(Constants.ACK_QUEUE).build();
    }
//    创建交换机
    @Bean("ackExchange")
    public DirectExchange ackExchange(){
        return ExchangeBuilder.directExchange(Constants.ACK_EXCHANGE).build();
    }
//    队列和交换机进行绑定
    @Bean("ackBinding")
    public Binding ackBinding(@Qualifier("ackQueue") Queue queue,@Qualifier("ackExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("ack");
    }

发送消息

java 复制代码
    @RequestMapping("/ack")
    public String ack(){
        rabbitTemplate.convertAndSend(Constants.ACK_EXCHANGE,"ack","consumer ack ");
        System.out.println("消息发送成功");
        return "消息发送成功";
    }

消费者接收消息,如果出现异常时,进行异常的捕获,并且将消息直接抛弃

java 复制代码
@Component
public class AckListener {

    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void ListenerQueue(Message message, Channel channel) throws IOException {

        System.out.printf("接收到的消息:%s, deliveryTag: %d \n",new String(message.getBody(), StandardCharsets.UTF_8)
                ,message.getMessageProperties().getDeliveryTag());

        long getDeliveryTag=message.getMessageProperties().getDeliveryTag();
        try {
            System.out.println("业务逻辑处理");
            System.out.println("消息发送完成");
//            multiple如果为true,则是批量确认,false则不是
            channel.basicAck(getDeliveryTag,false);
        }catch (Exception e){

            //requeue如果为true,消息发送失败会回到队列,重新发送,如果false,则会直接抛弃
            channel.basicNack(getDeliveryTag,false,false);
        }
    }
}

自动确认模式(AUTO)

在配置文件中进行auto的配置

java 复制代码
@RabbitListener(queues = Constants.ACK_QUEUE)
    public void ListenerQueue2(Message message)  {

        System.out.printf("接收到的消息:%s, deliveryTag: %d \n",new String(message.getBody(), StandardCharsets.UTF_8)
                ,message.getMessageProperties().getDeliveryTag());


    }

持久性

当服务重启或者崩溃时,你的消息还能被恢复出来

交换机持久化

设置durable为true

java 复制代码
    @Bean("ackExchange")
    public DirectExchange ackExchange(){
        return ExchangeBuilder.directExchange(Constants.ACK_EXCHANGE).durable(true).build();
    }

队列持久化

设置durable参数中传入队列,,表示rabbitmq会将队列中的数据保存在磁盘中,只要队列存在,那么消息就会存在

java 复制代码
    @Bean("ackQueue")
    public Queue ackQueue(){
        return QueueBuilder.durable(Constants.ACK_QUEUE).build();
    }

消息持久化

rabbitmq会把消息内容写到磁盘中的持久化日志

看一下MessageDeliveryMode的源代码

java 复制代码
    @RequestMapping("/pres")
    public String pres(){
        Message message=new Message("pres 持久化测试".getBytes(),new MessageProperties());
        message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        rabbitTemplate.convertAndSend(Constants.PRES_EXCHANGE,"pres",message);
        return "消息发送成功";
    }

发送方确认

RabbitMQ 提供的一种可靠投递机制,用于让消息生产者确认消息是否成功达到交换机,是否成功的路由到队列,是用于生产者到交换机之间的消息确认

confirm确认模式

java 复制代码
publisher-confirm-type: correlated #消息确认机制

如果消息发送成功,ack为true,如果为发送失败,ack为false,并打印出日志的原因

java 复制代码
@Bean("confirmRabbitTemplate")
    public RabbitTemplate confirmRabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory);
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if(b){
                    System.out.printf("消息接收成功,id:%s:",correlationData.getId());
                }else {
                    System.out.printf("消息接收失败,id:%s:,原因:%s",correlationData.getId(),s);
                }
            }
        });
    return rabbitTemplate;
 }
java 复制代码
    @RequestMapping("confirm")
    public String confirm(){
        CorrelationData correlationData=new CorrelationData(UUID.randomUUID().toString());
        confirmRabbitTemplate.convertAndSend(Constants.CONFIRM_EXCHANGE,"123","confirm .....",correlationData);
        return "消息发送成功";
    }

return退回模式

在消息的退回时也需要配置

java 复制代码
@Bean("confirmRabbitTemplate")
    public RabbitTemplate confirmRabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory);
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if(b){
                    System.out.printf("消息接收成功,id:%s:",correlationData.getId());
                }else {
                    System.out.printf("消息接收失败,id:%s:,原因:%s",correlationData.getId(),s);
                }
            }
        });

        rabbitTemplate.setMandatory(true);//不设置这个为true,消息无法进行退回
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                System.out.println("消息退回:" +returnedMessage);
            }
        });
        return rabbitTemplate;
    }

重试机制

在消息传递的过程中出现了许多的问题,服务不可用,资源不足等,这些问题可能导致消息处理失败,rabbitmq为我们提供了消息重试机制,允许消息发送失败后重新进行发送,如果是程序的逻辑出现问题,那么多次的重试也是没有结果的,可以在配置文件中设置重试的次数

设置重试的次数时5

配置队列和交换机

java 复制代码
    @Bean("retryQueue")
    public Queue retryQueue(){
        return QueueBuilder.durable(Constants.RETRY_QUEUE).build();
    }

    @Bean("retryExchange")
    public DirectExchange retryExchange(){
        return ExchangeBuilder.directExchange(Constants.RETRY_EXCHANGE).build();
    }
    @Bean("retryBlinding")
    public Binding retryBlinding(@Qualifier("retryQueue") Queue queue,
                                 @Qualifier("retryExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("123");
    }

发送消息

java 复制代码
    @RequestMapping("retry")
    public String retry(){
        CorrelationData correlationData=new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend(Constants.RETRY_EXCHANGE,"123","retry.....",correlationData);
        return "消息发送成功";
    }

接收消息

我们在接收的时候设置了 int sum=3/0这个错误代码

在接收的时候会报错,会使消息进行重新发送

java 复制代码
@Component
public class RetryListener {

    @RabbitListener(queues = Constants.RETRY_QUEUE)
    public void retryListener(Message message){
        long deliveryTag=message.getMessageProperties().getDeliveryTag();
        System.out.printf("接收到的消息:%s,deliveryTag:%s\n",new String(message.getBody()),deliveryTag);
        int sum=3/0;
        System.out.println("业务处理完成");

/**
 *         如果对异常的信息进行了捕获,那么就不会进行重试
 *          try {
 *             int sum=3/0;
 *             System.out.println("业务处理完成");
 *         }catch (Exception e){
 *             System.out.println("业务处理失败");
 *         }
 */
    }
}

消息接收的结果,我们发现消息进行了重新的发送

但是如果我们在接收的时候对异常进行捕获,就不会进行消息重试

我们将模式改为手动确认,重试机制并没有为我们进行消息的重新发送

java 复制代码
@RabbitListener(queues = Constants.ACK_QUEUE)
    public void ListenerQueue(Message message, Channel channel) throws IOException {

        System.out.printf("接收到的消息:%s, deliveryTag: %d \n",new String(message.getBody(), StandardCharsets.UTF_8)
                ,message.getMessageProperties().getDeliveryTag());

        long getDeliveryTag=message.getMessageProperties().getDeliveryTag();
        try {
            int sum=3/0;
//            multiple如果为true,则是批量确认,false则不是
            channel.basicAck(getDeliveryTag,false);
        }catch (Exception e){

            //requeue如果为true,消息发送失败会回到队列,重新发送,如果false,则会直接抛弃w
            channel.basicNack(getDeliveryTag,false,false);
        }
    }

⼿动确认模式下,消费者需要显式地对消息进⾏确认.如果消费者在处理消息时遇到异常,可以选择不确认消息使消息可以重新⼊队,重试的控制权在应用本身,而不是rabbimq的内部机制

TTL-过期时间

ttl就是过期时间,rabbitmq可以对消息和队列设置过期时间,当消息到达存活的时间,如果还没有被消费的话,就会自动的进行删除,

设置消息的过期时间

设置队列和交换机

java 复制代码
    @Bean("ttlQueue")
    public Queue ttlQueue(){
        return QueueBuilder.durable(Constants.TTL_QUEUE).build();
    }

    @Bean("ttlExchange")
    public DirectExchange ttlExchange(){
        return ExchangeBuilder.directExchange(Constants.TTL_EXCHANGE).build();
    }

    @Bean("ttlBlinding")
    public Binding ttlBlinding(@Qualifier("ttlQueue") Queue queue,
                               @Qualifier("ttlExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("123");
    }

发送消息

在发送消息的时候设置过期时间,设置过期时间为10秒

java 复制代码
//    设置消息的ttl
    @RequestMapping("ttl")
    public String ttl(){
        System.out.println("ttl");
        String ttlTime="10000"; //10秒
        rabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"123","ttl...........",message -> {
            message.getMessageProperties().setExpiration(ttlTime);
            return message;
        });
        return "消息发送成功";
    }

发送一条消息

十秒后

注:如果将ttl设置为0,如果消息没有直接的投递到消费者,那么消息会直接过期

设置队列的过期时间

设置过期队列

java 复制代码
    @Bean("ttlQueue2")
    public Queue ttlQueue2(){

        //设置过期队列的方法一
        int ttlTime=10000;//设置过期时间为20秒
        return QueueBuilder.durable(Constants.TTL_QUEUE2).ttl(ttlTime).build();

        //设置过期队列方法二
//        Map<String, Object> argument=new HashMap<>();
//        argument.put("x-message-ttl",20000);
//        return QueueBuilder.durable(Constants.TTL_QUEUE2).withArguments(argument).build();

    }

将过期队列和交换机进行绑定

java 复制代码
    @Bean("ttlBlinding2")
    public Binding ttlBlinding2(@Qualifier("ttlQueue2") Queue queue,
                               @Qualifier("ttlExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("123");
    }

发送消息

java 复制代码
//    设置队列的ttl
    @RequestMapping("ttl2")
    public String ttl2(){
        System.out.println("ttl2");
        rabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"123","ttl2...........");
        return "消息发送成功";
    }

发送消息

10秒后

我们发现队列中多了一个标签

显示过期的时间是10s

队列ttl的方法:即消息过期,消息立刻从对列中进行删除

消息ttl的方法:即消息过期,消息也不会立刻从队列中进行删除,在即将投递到消费者之前进行判定

两者不同的原因:

设置队列的过期时间,队列中已经过期的消息在队列的头部,只需要定期扫描头部进行删除

设置消息的过期时间,每条消息的过期时间不同,如果要删除消息需要扫描整个队列,所以不如等到消息即将要被消费的时候在来进行判定消息是否已经过期

死信队列

消息由各种原因无法被消费,就是死信

java 复制代码
@Configuration
public class DelConfig {

    @Bean("normalQueue")
    public Queue normalQueue(){
        return QueueBuilder.durable(Constants.NORMAL_QUEUE)
                .ttl(10000)
                .deadLetterExchange(Constants.DEL_EXCHANGE)
                .deadLetterRoutingKey("del")
                .maxLength(10L)
                .build();
    }

    @Bean("normalExchange")
    public DirectExchange normalExchange(){
        return ExchangeBuilder.directExchange(Constants.NORMAL_EXCHANGE).build();
    }

    @Bean("normalBlinding")
    public Binding normalBlinding(@Qualifier("normalQueue") Queue queue,
                                  @Qualifier("normalExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("normal");
    }

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

    @Bean("delExchange")
    public DirectExchange delExchange(){
        return ExchangeBuilder.directExchange(Constants.DEL_EXCHANGE).build();
    }

    @Bean("delBlinding")
    public Binding delBlinding(@Qualifier("delQueue") Queue queue,
                                  @Qualifier("delExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("del");
    }

}

发送消息到普通队列

java 复制代码
    @RequestMapping("del")
    public String del(){
        System.out.println("del");
        for (int i = 0; i < 20; i++) {
            rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","del...........");
        }
        return "消息发送成功";
    }

接收消息,在接收消息的代码出写一个异常,让消息接收失败,使消息到死信队列中

java 复制代码
@Component
public class DelListener {

    @RabbitListener(queues = Constants.DEL_QUEUE)
    public void delMessage1(Message message){
        System.out.printf("接收到的消息:%s, deliveryTag: %d \n",new String(message.getBody(), StandardCharsets.UTF_8)
                ,message.getMessageProperties().getDeliveryTag());
    }

    @RabbitListener(queues = Constants.NORMAL_QUEUE)
    public void delMessage2(Message message, Channel channel) throws Exception {

        System.out.printf("接收到的消息:%s, deliveryTag: %d \n",new String(message.getBody(), StandardCharsets.UTF_8)
                ,message.getMessageProperties().getDeliveryTag());

        long getDeliveryTag=message.getMessageProperties().getDeliveryTag();
        try {
            int num=3/0;
            System.out.println("消息发送完成");
            channel.basicAck(getDeliveryTag,false);
        }catch (Exception e){
            channel.basicNack(getDeliveryTag,false,false);
        }

    }
}

从死信队列接收到了消息

延迟队列

即消息发送后,并不希望立刻的让消费者拿到消息,而是等待特定的时间之后,才让消费者拿到消息消费

rabbitmq本身没有直接支持延迟队列的功能,但是我们可以通过TTL+死信队列来模拟实现延迟队列,通过发送消息到有过期时间的队列,将延迟时间和过期时间设置为一样,当消息过期后,进入了死信队列,这时候消费者接收死信队列的消息,就实现了延迟队列

定义一个普通队列其参数绑定一个死信交换机

java 复制代码
//    使用ttl+死信队列模拟延迟队列
    @Bean("normalQueue2")
    public Queue normalQueue2(){
        return QueueBuilder.durable(Constants.NORMAL_QUEUE2)
//                .ttl(10000)
                .deadLetterExchange(Constants.DEL_EXCHANGE2)
                .deadLetterRoutingKey("del2")
                .build();
    }

    @Bean("normalExchange2")
    public DirectExchange normalExchange2(){
        return ExchangeBuilder.directExchange(Constants.NORMAL_EXCHANGE2).build();
    }

    @Bean("normalBlinding2")
    public Binding normalBlinding2(@Qualifier("normalQueue2") Queue queue,
                                  @Qualifier("normalExchange2") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("normal2");
    }

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

    @Bean("delExchange2")
    public DirectExchange delExchange2(){
        return ExchangeBuilder.directExchange(Constants.DEL_EXCHANGE2).build();
    }

    @Bean("delBlinding2")
    public Binding delBlinding2(@Qualifier("delQueue2") Queue queue,
                               @Qualifier("delExchange2") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("del2");
    }

发送消息到普通队列,但是接收消息绑定的是死信队列

java 复制代码
@Component
public class DelListener {

    @RabbitListener(queues = Constants.DEL_QUEUE)
    public void delMessage1(Message message){
        System.out.printf("接收到的消息:%s, deliveryTag: %d \n",new String(message.getBody(), StandardCharsets.UTF_8)
                ,message.getMessageProperties().getDeliveryTag());
    }
}

但是会存在问题:

当我们发两条消息的时候,一条消息的延迟时间20秒,第二条消息的延迟时间10秒,当两条消息都被丢到队列中,只会检查队头的元素是否过期,当检测到队头已过期,把它丢到死信交换机中,执行第二条消息,我们发现第二条早已经过期,消息会随着第一条消息一起被死信交换机发送出去,时间是相同的,在发送消息的时候并不会因为第二条消息的延迟时间短而先得到执行

java 复制代码
@RequestMapping("delay")
    public String delay(){
        System.out.println("delay");
        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay....."+new Date(),
                message ->{message.getMessageProperties().setDelayLong(10000L);
                return message;});

        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay....."+new Date(),
                message ->{message.getMessageProperties().setDelayLong(20000L);
                    return message;});

        return "消息发送成功";
    }

Rabbitmq官方为我们提供了一个插件来实现延迟队列

Releases · rabbitmq/rabbitmq-delayed-message-exchange

打开该链接下载.zip文件

将插件复制到你的插件文件夹中,然后输入命令进行下载

如何找到你的插件文件夹呢?在rabbirmq的官网上也进行了介绍

进入rabbitmq的管理界面中查看插件是否下载成功

如果你的类型中出现这个的话,则说明的你的插件下载成功

使用插件实现延迟队列

在构建交换机的时候记得启动延迟功能

java 复制代码
@Bean("delayQueue")
    public Queue delayQueue(){
        return QueueBuilder.durable(Constants.DELAY_QUEUE).build();
    }
    @Bean("delayExchange")
    public DirectExchange delayExchange(){
        return ExchangeBuilder.directExchange(Constants.DELAY_EXCHANGE).delayed().build();
    }

    @Bean("delayBlinding")
    public Binding delayBlinding(@Qualifier("delayQueue") Queue queue,
                                 @Qualifier("delayExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("delay");
    }

在发送消息的时候设置每条消息的延迟时间

java 复制代码
@RequestMapping("delay")
    public String delay(){
        System.out.println("delay");
        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay....."+new Date(),
                message ->{message.getMessageProperties().setDelayLong(10000L);
                return message;});

        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay....."+new Date(),
                message ->{message.getMessageProperties().setDelayLong(20000L);
                    return message;});

        return "消息发送成功";
    }

接收消息

java 复制代码
    @RabbitListener(queues = Constants.DELAY_QUEUE)
    public void delayListener2(Message message){
        System.out.printf("[ %s: ], %tc:接收时间",new String(message.getBody()),new Date());
        System.out.println("消息接收成功");
    }

当我们先发延迟高的消息,再发延迟低的消息,看会不会出现ttl+死信模拟实现延迟队列的bug

java 复制代码
@RequestMapping("delay")
    public String delay(){
        System.out.println("delay");
        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay1111....."+new Date(),
                message ->{message.getMessageProperties().setDelayLong(20000L);
                    return message;});

        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay5555....."+new Date(),
                message ->{message.getMessageProperties().setDelayLong(10000L);
                    return message;});

        return "消息发送成功";
    }

延迟消息测试成功

事务机制

生产者在发送消息时,可以通过事务来保证消息的可靠性,要么全部发送成功到broker,要么全部失败回滚,不会丢失或者部分发送

要启动事务机制,首先配置事务管理器

java 复制代码
@Configuration
public class TransTemplateConfig {


    @Bean
    public RabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory cachingConnectionFactory){
        return new RabbitTransactionManager(cachingConnectionFactory);
    }

    @Bean
    public RabbitTemplate transRabbitTemplate(CachingConnectionFactory cachingConnectionFactory){
        RabbitTemplate rabbitTemplate=new RabbitTemplate(cachingConnectionFactory);
        rabbitTemplate.setChannelTransacted(true);
        return rabbitTemplate;
    }
}

申明队列

java 复制代码
    @Bean("transQueue")
    public Queue transQueue(){
        return QueueBuilder.durable(Constants.TRANS_QUEUE).build();
    }

发送消息

@Transactional:Spring事务的注解,用来申明要么全部成功,要么全部失败

java 复制代码
    @Transactional
    @RequestMapping("trans")
    public String trans(){
        System.out.println("delay");
        transRabbitTemplate.convertAndSend("",Constants.TRANS_QUEUE,"trans.......");
        transRabbitTemplate.convertAndSend("",Constants.TRANS_QUEUE,"trans.......");
        int sum=2/0;
        transRabbitTemplate.convertAndSend("",Constants.TRANS_QUEUE,"trans.......");
        return "消息发送成功";
    }

消息发送全部失败

如果我们把该注解去了,事务机制失效,则会发送前两条消息

消息分发

创建交换机和队列

java 复制代码
@Configuration
public class QosConfig {
    @Bean("qosQueue")
    public Queue qosQueue(){
        return QueueBuilder.durable(Constants.QOS_QUEUE).build();
    }
    @Bean("qosExchange")
    public DirectExchange qosExchange(){
        return ExchangeBuilder.directExchange(Constants.QOS_EXCHANGE).delayed().build();
    }

    @Bean("qosBlinding")
    public Binding qosBlinding(@Qualifier("qosQueue") Queue queue,
                                 @Qualifier("qosExchange") DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("qos");
    }
}

生产者发送的消息,经过交换机路由后,被分发到对应的队列中,最终由消费者接收消息进行处理

限流

当我们发送10条消息的时候

java 复制代码
    @RequestMapping("qos")
    public String qos(){
        System.out.println("qos");
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend(Constants.QOS_EXCHANGE,"qos","qosqosqos");
        }
        return "消息发送成功";
    }

只接收5条

负载均衡

当多个消费者监听同一个队列的时候,rabbitmq需要把队列中的消息平均的分配给所有监听该队列的消费者,实现负载均衡

配置两个消费者,每个消费者每次只接收一条消息

java 复制代码
@Component
public class QosListener {
    @RabbitListener(queues = Constants.QOS_QUEUE)
    public void delayListener1(Message message, Channel channel) throws Exception{
        try {
            System.out.println("收到消息:" + message);

            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            System.out.println("1111111");
        } catch (Exception e) {
            // 拒绝消息并重新入队
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            System.err.println("处理失败,消息重新入队:" + e.getMessage());

        }

    }
    @RabbitListener(queues = Constants.QOS_QUEUE)
    public void delayListener2(Message message, Channel channel) throws Exception {
        try {
            System.out.println("收到消息:" + message);

            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            System.out.println("22222222222");
        } catch (Exception e) {
            // 拒绝消息并重新入队
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            System.err.println("处理失败,消息重新入队:" + e.getMessage());

        }
    }
}
相关推荐
半旧夜夏16 小时前
【分布式缓存】Redis持久化和集群部署攻略
java·运维·redis·分布式·缓存
还是大剑师兰特18 小时前
Hadoop面试题及详细答案 110题 (106-110)-- Hadoop高级与实战
大数据·hadoop·分布式
壹佰大多21 小时前
【Redisson分布式锁源码分析-3】
数据结构·分布式·mysql·spring·spring cloud·wpf·lua
不会写代码的ys1 天前
仿RabbitMQ实现消息队列(一)--项目介绍
分布式·rabbitmq
数据库学啊1 天前
分布式数据库架构设计指南:TDengine如何支持10亿级数据点的水平扩展
数据库·分布式·时序数据库·数据库架构·tdengine
兜兜风d'1 天前
RabbitMQ死信队列详解
c++·rabbitmq·java-rabbitmq
mit6.8241 天前
[VT-Refine] 强化学习工作流 | 分布式-近端策略优化(DPPO)
分布式·算法
Damon小智1 天前
HarmonyOS 5 开发实践:分布式任务调度与设备协同架构
分布式·架构·harmonyos
凯子坚持 c1 天前
【星光不负 码向未来 | 万字解析:基于ArkUI声明式UI与分布式数据服务构建生产级跨设备音乐播放器】
分布式·ui