RabbitMQ保证消息的可靠性原理解析

1、中间件消息传递可能出现的问题

在这里插入图片描述
从上图我们可以看到,使用生产者、消息中间件、消费者在进行消息传递时会涉及很多重要的中间环节的处理,如果这些中间环节不够健壮,就无法保证消息的正常传递、处理、保存、及响应。

做一下思维发散, 上图中那些环节可能会出现问题,具体是什么问题?

  • 1、生产者消息一定能成功发送到交换机麽?
  • 2、交换机中的消息一定可以路由到Queue麽?
  • 3、Queue中的消息如果无法持久,服务异常或者网络中断等故障发生后, 服务恢复正常之后,消息是不是就丢了?
  • 4、如何保证消费者成功消费了Queue中的消息呢?

2、RabbitMQ是处理上述问题的机制

2.1 生产者如何保证消息一定成功发送到Exchange

在这里插入图片描述
之前有一种方式就是这个Publisher Confirms我们没有介绍, 这种就是生产者确认的方式保证我生产者一定可以知道消息是不是成功的发送到了交换机,就算发送失败了, 我也可以通过补偿机制进行处理,RabbitMQ提供了以下3种具体的实现策略:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
核心代码就是需要开启Publisher的confirm机制,可以通过Confirm效果保证消息一定送达到Exchange,方式上推荐对于效率影响最低的异步回调方式

// 开启confirm机制
channel.confirmSelect();
// 监听器监听是否发送成功并进行处理
channel.addConfirmListener(new ConfirmListener() {
    // Exchange确认消息收到
    @Override
    public void handleAck(long l, boolean b) throws IOException {
        System.out.println("消息已成功收到...");
    }

    // Exchange未收到消息
    @Override
    public void handleNack(long l, boolean b) throws IOException {
        System.out.println("没收到消息,怎么回事,赶紧处理一下吧...");
    }
});

2.2 Exchange保证消息一定路由到Queue

// 开启returns机制, 使用异步回调通知交换机消息是否接收成功
channel.addReturnListener(new ReturnListener() {
    @Override
    public void handleReturn(int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
        System.out.println("消息路由到Queue失败,请尽快处理...");
    }
});
// 发送消息

2.3 Queue中消息的持久化存储

队列中消息如何不能持久化存储,当发生服务异常、宕机等情况, 服务恢复之后消息肯定就丢了, 数据丢失是不能接受的, rabbitMQ提供了开启队列持久化存储的属性,主要是通过AMQP.BasicProperties来进行设置是否开启持久化存储。

// 9、设置Queue开启持久化存储
AMQP.BasicProperties props = new AMQP.BasicProperties()
        .builder()
        .deliveryMode(2)
        .build();

2.4 消费者正常消费Queue中的消息

关闭自动ack, 使用异步回调进行消息是否成功消费的ack确认。

DefaultConsumer callBack = new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.out.println("消费queue-1队列的消息: " + new String(body, "UTF-8"));
        channel.basicAck(envelope.getDeliveryTag(), false);
    }
};
channel.basicConsume(Producer.QUEUE_NAME_ONE, false, callBack);

3、Springboot项目中如何进行rabbitMQ消息可靠性的处理

3.1 Springboot中如何处理Pulisher confirm

首先在application.yml文件中配置publisher-confirm-type为correlated

spring:
  rabbitmq:
    # 开启publisher confirm机制
    publisher-confirm-type: correlated

在这里插入图片描述
编写生产者测试类,进行测试

package com.kkarma.confirm;

import com.kkarma.config.RabbitMQConfig;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
public class Publisher {

    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public Publisher(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Test
    public void push() throws Exception {
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if(b){
                    System.out.println("消息已经送达到交换机!!");
                }else{
                    System.out.println("消息没有送达到Exchange,请尽快处理...");
                }
            }
        });

        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "com.kkarma.controller", "msg push need confirm");
        System.out.println("message send success......");
//        System.in.read();
    }
}

3.2 Springboot中如何处理queue return

application.yml文件中配置信息

spring:
  rabbitmq:
    # 开启Return机制
    publisher-returns: true

编写生产者测试类,进行测试

package com.kkarma.returns;

import com.kkarma.config.RabbitMQConfig;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

@SpringBootTest
public class Publisher {

    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public Publisher(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Test
    public void push() throws Exception {
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                String msg = message.getBody().toString();
                System.out.println("消息: " + msg + "路由到queue失败,请尽快处理");
            }
        });
        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,
                "my.com.kkarma.controller",
                "Exchange消息路由到queue,需要返回接收结果...");
        System.out.println("消息发送成功。。。");
        System.in.read();
    }
}

测试结果
在这里插入图片描述

3.3 Springboot中如何处理queue消息持久化

在生产者中借助RabbitTemplate的convertAndSend方法中的MessagePostProcessor参数属性来开启持久化

package com.kkarma.persistence;


import com.kkarma.config.RabbitMQConfig;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
public class Publisher {

    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public Publisher(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Test
    public void push() {
        MessagePostProcessor processor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                MessageProperties props = message.getMessageProperties();
                // 设置queue消息持久化
                props.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                return message;
            }
        };
        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "con.kkarma.controller", "开启信息持久化", processor);
        System.out.println("消息发送成功。。。");
    }
}


3.4 Springboot中如何处理消费正常消费消息

application.yml中开启手动ack

spring:
  rabbitmq:
    # 开启消费者手动ack
    listener:
      direct:
        acknowledge-mode: manual
      # 设置消息流控  
      simple:
        prefetch: 10
        # concurrency: 5
        # 设置消费者并发数
        # max-concurrency: 5

消费者端代码实现

package com.kkarma.consumer;


import com.kkarma.config.RabbitMQConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Consumer {


    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public Consumer(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @RabbitListener(queues = RabbitMQConfig.QUEUE_NAME)
    public void pull(String msg, Channel channel, Message message) throws Exception {
        System.out.println("消费消息内容: " + msg);
        String correlationId = message.getMessageProperties().getCorrelationId();
        System.out.println("唯一标识: " + correlationId);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        System.in.read();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值