RabbitMQ高级特性

RabbitMQ高级特性

1、消息的可靠性传递

生产者

在使用rabbitmq的时候,消息的发送方会杜绝消息的丢失或者投递失败的场景,所以rabbitmq为我们提供了两种解决方式:
1、confirm
2、return

rabbitmq整个消息投递的路径为:
producer—>rabbitmq broker—>exchange—>queue—>consumer

当消息从producer—>exchange 会返回一个confirmReturn;
消息从producer—>queue 会返回一个returnCallback
我们利用这两个来保证消息的可靠性投递

java 代码:

import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class RabbitInitConfig {
    RabbitTemplate rabbitTemplate;

    @Primary
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory);
        this.rabbitTemplate=rabbitTemplate;
        initRabbitmq();
        rabbitTemplate.setMessageConverter(messageConverter());
        return rabbitTemplate;
    }

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    public void initRabbitmq(){
        //消息抵达broke触发回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("confirm方法被触发了:"+s);

                if(b){
                    //接收成功
                    System.out.println("接收消息成功"+s);
                }else {
                    System.out.println("接收消息失败"+s);
                }
            }
        });

        //消息return机制
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                System.out.println("return触发"+returnedMessage);
            }
        });
    }

}

配置文件

#开始confirm模式
spring.rabbitmq.publisher-confirms=true
#开始return模式
spring.rabbitmq.publisher-returns=true

消费者

consumer ACK

消费者有三种接收消息的方式:

  • ackknowledge=none 自动确认
  • ackknowledge=manual 手动确认
  • ackknowledge=auto 根据异常情况确认

默认自动确认,消息被consumer取到后,会自动确认,同时将队列中的该消息移除
我们也可以修改消息为手动确认,手动ack需要调用channel.basicAck()
如果出现异常,可以调用channel.basicNack(),进行重复发送
一般推荐手动确认,保证消息的可靠性

配置

#手动确认ack
spring.rabbitmq.listener.direct.acknowledge-mode=manual

2、消费端限流

RabbitMQ高级特性_第1张图片
配置

#设置消费端一次拉去多少消息
spring.rabbitmq.listener.simple.prefetch=1000

消费端的确认模式一定为手动确认channel.basicAck()

3、延时队列

延时队列=死信队列+TTL

延时队列在工作中会经常用到:如下订单30分钟未支付…

死信队列

RabbitMQ高级特性_第2张图片
生产者将消息发送到队列后,消息一定时间内未被消费,超过了TTL设置的时间,就会进入到死信交换机,然后由相应的队列进行消费

代码

package org.ww.boot_producer_rabbitmq.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitmqConfig {

    //------------------------------------延时队列---------------------------------------------------

    @Bean("createOrderQueue")
    public Queue createOrderQueue(){
        return QueueBuilder.durable("order.delay.order")
                .deadLetterExchange("order-event-exchange")
                .deadLetterRoutingKey("order.relase.order")
                .ttl(5000).build();
    }

    @Bean("relaseQueue")
    public Queue relaseQueue(){
        return QueueBuilder.durable("order.relase.order.queue").build();
    }

    //死信交换机
    @Bean("exchange")
    public Exchange deadInfoExchange(){
        return ExchangeBuilder.topicExchange("order-event-exchange").durable(true).build();
    }

    //创建绑定关系
    @Bean
    public Binding createBind(@Qualifier("createOrderQueue") Queue createQueue,@Qualifier("exchange") Exchange exchange){
        return BindingBuilder.bind(createQueue).to(exchange).with("order.create.#").noargs();
    }

    @Bean
    public Binding releaseOrderBingding(@Qualifier("relaseQueue") Queue relaseQueue,@Qualifier("exchange") Exchange exchange){
          return BindingBuilder.bind(relaseQueue).to(exchange).with("order.relase.#").noargs();
    }
}

监听

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;

@Component
public class DeadMsgConfig{
//    @RabbitListener(queues = "order.delay.order")
//    @RabbitHandler
//    public void onMessage(String msg, Channel channel,Message message) throws Exception {
//
//        try {
//            System.out.println("延时队列处理业务逻辑"+msg.toString());
//            //手动接收
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
//        }catch (Exception e){
//            //拒绝
//            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
//            //第三个参数,重回队列,如果设置为true,则消息重新回到队列,broker会重新发送该消息给消费端
////            channel.basicNack(message.getMessageProperties().getDeliveryTag(),true, true);
//            e.printStackTrace();
//        }
//
//    }

    @RabbitListener(queues = "order.relase.order.queue")
    @RabbitHandler
    public void relase(String msg, Channel channel,Message message) throws Exception {

        try {
            System.out.println("最终队列处理业务逻辑----"+msg.toString());
            //手动接收
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            //拒绝
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
            //第三个参数,重回队列,如果设置为true,则消息重新回到队列,broker会重新发送该消息给消费端
//            channel.basicNack(message.getMessageProperties().getDeliveryTag(),true, true);
            e.printStackTrace();
        }

    }
}

4、消息的幂等性

幂等性:指一次或多次请求一个资源,对于资源本身应该具有相同的结果,也就是说,其多次执行的结果和进行一次执行的结果应该相同

在MQ中指,多次消费一条消息与消费一条消息的结果一样

消息幂等性保障:可以使用乐观锁机制

5、消息的积压

解决:
1、上多个消费者
2、将消息先取出来放数据库里在慢慢处理

6、重复消费

在第一次消费该消息时,以messageId为key放入到redis中(设置个过期时间),下次在消费之前进行判断这个key有没有在redis中存在,存在就进行手动确认。

你可能感兴趣的:(rabbitmq,java,rabbitmq)