RabbitMQ—进阶篇

1、消息可靠性投递

在使用RabbitMQ的时候,作为消费发送方希望杜绝任何消息丢失或者投递失败场景。所以RabbitMQ为我们提供了两种方式用来控制消息的投可靠模式。

消息投递顺序步骤:
在这里插入图片描述
为了确保消息的可靠性投递,提供了如下方式。

  • confirm 确认模式
  • return 退回模式

(1)确认模式(Confirm)

前提是必须要开启确认模式!!!这样才能保证消息从生产者交换机的消息可靠性投递。否则消息在传递的过程中从队列中消失

生产者的配置文件

spring:
  rabbitmq:
    host: 192.168.31.35
    #开启rabbitMQ的生产方确认模式
    publisher-confirm-type: correlated

设置RabbitTemplate的确认回调函数

@SpringBootTest(classes = ProductApp.class)
public class TestConfirm {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void test01(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback(){
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if (b==false){
                    System.out.println("继续发送消息"+b);
                }
            }
        });
        rabbitTemplate.convertAndSend("fyx01","","hello");
    }
    }

(2)退回模式(return)

1、开启回退机制

server:
  port: 8080
spring:
  rabbitmq:
    host: 192.168.34.35
    #开启rabbitMQ的生产方确认模式
    publisher-confirm-type: correlated
    # 开启发布者退回模式
    publisher-returns: true

2、设置RabbitTemplate回调的函数

 @Test
    void test02() {
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {

            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                System.out.println("消息从交换机到队列失败!!!"+returnedMessage.getReplyText());
            }
        });
        rabbitTemplate.convertAndSend("fyx01","aaa","消息发送成功!");
}

2、Consumer ACK 客户端确认

它表示消费端consumer收到消息后的确认方式。

其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息队列中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用**channel.basicNack()**方法,让其自动重新发送消息。

在消费端配置手动开启确认模式

spring:
  rabbitmq:
    host: 192.168.31.35
    listener:
      simple:
        #表示手动确认
        acknowledge-mode: manual
        #表示自动确认模式
        # acknowledge-mode: none
@Component
public class Test01 {
 
    @RabbitListener(queues = "fyx01")
    public void listener(Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        byte[] body = message.getBody();
        String msg = new String(body);
        System.out.println(msg);
        //int a =10/0;
        System.out.println("处理逻辑业务");
        try {
        	//假如出现异常
            int a =10/0;
            System.out.println("处理逻辑业务");
            channel.basicAck(deliveryTag,true);//成功后从队列删除该信息
        } catch (IOException e) {
           //e.printStackTrace();
            //(long deliveryTag, boolean multiple, boolean requeue: 是否让队列再次发送该消息。
            channel.basicNack(deliveryTag,true,true);
        }
    }
}

3、如何保证消息的可靠性

  1. 保证消息从发送者到交换机的可靠性: 使用Confirm确认机制。
  2. 保证消息从交换机到队列的可靠性; 使用return回退机制。
  3. 消息在队列中的可靠性。 设置队列和消息的持久化。
  4. 保证消息从队列到消费者的可靠性。 使用消费端的手动确认机制。

4、消费端限流

目的就是为了防止大量的数据堆积在消息队列,而消费端的业务逻辑耗时很长,就不能一下让太多的访问消息到达消费端,严重了就服务器挂掉。

修改配置文件

1. 必须为手动确认模式。
2. 必须配置限流的个数。
spring:
  rabbitmq:
    host: 192.168.31.35
    listener:
      simple:
        #表示手动确认
        acknowledge-mode: manual
        #表示自动确认模式
        # acknowledge-mode: none
        # 设置每次消费的个数。
        prefetch: 10

测试

@Component
public class Test01 {
 
    @RabbitListener(queues = "fyx01")
    public void listener(Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        byte[] body = message.getBody();
        String msg = new String(body);
        System.out.println(msg);

        try {
            //int a =10/0;
            System.out.println("处理逻辑业务");
            //channel.basicAck(deliveryTag,true);//成功后从队列删除该信息
        } catch (Exception e) {
           //e.printStackTrace();
            //(long deliveryTag, boolean multiple, boolean requeue: 是否让队列再次发送该消息。
            channel.basicNack(deliveryTag,true,true);
        }
    }
}

5、TTL(设置队列的存活时间)

  1. 设置队列过期;
  2. 设置消息的过期;该消息必须在队列的头部时才会被移除。

1、给队列中的单个元素设置过期时间

    @Test
    void test02() {
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {

            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                System.out.println("消息从交换机到队列失败!!!"+returnedMessage.getReplyText());
            }
        });
        for (int i=0;i<90;i++){
            rabbitTemplate.convertAndSend("amq.direct","","消息发送成功!"+i);
        }
}
    //为队列设置过期时间  相当于该队列里面的消息都由过期时间
    @Test
    public void testSend(){
        rabbitTemplate.convertAndSend("myexchange","","hello xiaoxi");
    }
    //设置消息的过期时间 如果由设置了队列的过期时间 也设置了消息的过期时间 谁的过期时间短 以谁为准。
    //该消息必须在头部才能从队列中移除。
    @Test
    public void testSend02(){

        for(int i=0;i<10;i++) {
            if(i==3){
                MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        message.getMessageProperties().setExpiration("5000");
                        return message;
                    }
                };
                //String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor
                rabbitTemplate.convertAndSend("myexchange", "", "hello xiaoxi"+i, messagePostProcessor);
            }else {
                //String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor
                rabbitTemplate.convertAndSend("myexchange", "", "hello xiaoxi"+i);
            }
        }
    }
}

2、使用浏览器端给整个队列设置时间

在这里插入图片描述

6、通过代码创建队列和交换机以及绑定。

@Configuration
public class RabbitConfig {

    private final String exchange_name="myexchange";
    private final String queue_name="myqueue";
    //创建交换机对象
    @Bean
    public Exchange exchange(){
        Exchange exchange= ExchangeBuilder.fanoutExchange(exchange_name).durable(true).build();
        return exchange;
    }

    //创建队列
    @Bean(value = "queue")
    public Queue queue(){
        Queue queue= QueueBuilder.durable(queue_name).withArgument("x-message-ttl",20000).build();
        return queue;
    }

    //绑定交换机和队列
    @Bean
    public Binding binding(Queue queue,Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("").noargs();
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值