【rabbitmq 】如何保证消息可靠性

概述

在这里插入图片描述
Broker:接收和分发消息的应用,RabbitMQ Server 就是 Message Broker

Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个 vhost,每个用户在自己的 vhost 创建 exchange/queue 等

Connection:publisher/consumer 和 broker 之间的 TCP 连接

Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection 的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个 thread 创建单独的 channel 进行通讯,AMQP method 包含了 channel id 帮助客户端和 message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的Connection 极大减少了操作系统建立 TCP connection 的开销

Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到 queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)

Queue:消息最终被送到这里等待 consumer 取走

Binding:exchange 和queue 之间的虚拟连接,binding 中可以包含 routing key,Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据:接收和分发消息的应用,RabbitMQ Server 就是 Message Broker

1、rabbitmq如何保证消息可靠

首先我们知道一个完整的结构涉及到生产者,mq,消费者这三部分,mq解除了系统之间的耦合,但是会出现一些问题,比如现在是三部分,它们都是独立的,每一部分都会存在消息丢失的情况,所以要从这三部分一起解决此问题。如下图。

在这里插入图片描述

1.1 mq保证消息不丢失

对交换机,队列,消息进行持久化,持久化之后mq断电重启,消息还会被继续消费

1.1.1交换机、队列持久化
@Configuration
public class MQConfig {
    //队列
    public  final static String CONFIRM_QUEUE = "confirm1_queue";

    //交换机
    public  final static String CONFIRM_EXCHANGE = "confirm1_exchange";

    //routingKey
    public  final static String ROUTINGKEY = "routing_key";

    /**
     * durable:持久化
     * exclusive: 设置是否排他, true表示队列为排他的, 如果一个队列被设置为排他队列, 该队列仅对首次声明它的连接可见, 并在连接断开时自动删除,
     * autoDelete: 设置是否自动删除。为true 则设置队列为自动删除
     * @return
     */
    @Bean
    public Queue confirmQueue() {
        return new Queue(CONFIRM_QUEUE, true, false, false);
    }

    /**
     * 声明一个direct类型的交换机,默认就是持久化,可以点开源码看
     * @return
     */
    @Bean
    DirectExchange confirmExchange() {
        return new DirectExchange(MQConfig.CONFIRM_EXCHANGE);
    }

    /**
     * 绑定Queue队列到交换机,并且指定routingKey
     * @return
     */
    @Bean
    Binding bindingDirectExchangeCA() {
        return BindingBuilder.bind(confirmQueue()).to(confirmExchange()).with(ROUTINGKEY);
    }
}
1.1.2 消息持久化

springboot集成的rabbitmq的持久化,其实默认就实现了,且看源码:

@Override
    public void convertAndSend(String exchange, String routingKey, final Object object) throws AmqpException {
        convertAndSend(exchange, routingKey, object, (CorrelationData) null);
    }
  //点进去
 	@Override
	public void convertAndSend(String exchange, String routingKey, final Object object,
@Nullable CorrelationData correlationData) throws AmqpException {

		send(exchange, routingKey, convertMessageIfNecessary(object), correlationData);
	}
 
	protected Message convertMessageIfNecessary(final Object object) {
		if (object instanceof Message) {
			return (Message) object;
		}
		return getRequiredMessageConverter().toMessage(object, new MessageProperties());
	}

此时发现消息转换的时候,传入了一个MessageProperties对象

    public MessageProperties() {
        this.deliveryMode = DEFAULT_DELIVERY_MODE;
        this.priority = DEFAULT_PRIORITY;
    }
    
   public MessageProperties() {
        this.deliveryMode = DEFAULT_DELIVERY_MODE;
        this.priority = DEFAULT_PRIORITY;
    }
    
    static {
    //持久化
        DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;
        DEFAULT_PRIORITY = 0;
    }

从 DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;看出springboot默认已经对消息进行了持久化

1.2 消费者消息不丢失

通过手动ack机制,当消费者成功将消息消费成功后,返回消息给mq,告诉mq你发送的消息我已经消费成功了,mq将队列中的消息进行删除

channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

Channel.basicAck(用于肯定确认)
RabbitMQ 已知道该消息并且成功的处理消息,可以将其丢弃了

yaml

rabbitmq:
    template:
      mandatory: true #指定消息在没有被队列接收时是否强行退回还是直接丢弃
    listener:
      simple:
        retry:
          ####开启消费者异常重试
          enabled: true
          ####最大重试次数
          max-attempts: 5
          ####重试间隔次数
          initial-interval: 5000
        ####开启手动ack
        acknowledge-mode: manual
1.3 生产者消息不丢失

发布确认机制,生产者通过回调可以得知发送的消息是否发送到交换机

yml文件:

 publisher-confirm-type: correlated  # 发布消息成功到交换器后会触发回调方法
 publisher-returns: true #publisher-return模式可以在消息没有被路由到指定的queue时将消息返回,而不是丢弃
 template:
   mandatory: true #指定消息在没有被队列接收时是否强行退回还是直接丢弃

相关业务代码:

@Configuration
@Slf4j
public class RabbitConfigStudy implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setReturnCallback(this);
        return rabbitTemplate;
    }

    /**
     * 回调函数,生产者到交换机的回调
     *
     * @param correlationData 相关消息,可以给消息配置相关消息和全局id
     * @param ack
     * @param cause           交换机没有收到消息的原因
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String id = correlationData != null ? correlationData.getId() : "";
        if (ack) {
            log.info("交换机已经收到 id 为:{}的消息", id);
        } else {
            log.info("交换机还未收到 id 为:{}消息,由于原因:{}", id, cause);
        }
    }

    /**
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.error(" 消 息 {}, 被 交 换 机 {} 退 回 , 退 回 原 因 :{}, 路 由 key:{}", new
                String(message.getBody()), exchange, replyText, routingKey);
        // spring_returned_message_correlation:该属性是指退回待确认消息的唯一标识 ,也就是id,可以作为更新表
        System.out.println("消息:" + message.getMessageProperties().getHeader("spring_returned_message_correlation").toString());
    }
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
RabbitMQ 通过持久化和确认机制来保证消息可靠性。 在发送消息时,可以设置消息的 delivery mode 为 2,表示消息需要被持久化。持久化的消息会被写入磁盘,即使 RabbitMQ 服务器宕机或重启,消息也不会丢失。 在接收消息时,可以使用确认机制。当消费者成功处理了一条消息后,会向 RabbitMQ 发送确认消息。如果 RabbitMQ 收到确认消息,就会将该消息从队列中删除,否则该消息会被重新发送。通过确认机制,可以保证消息不会被重复消费。 以下是一个简单的 RabbitMQ 发送和接收消息的示例代码: ``` import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() # 声明队列 channel.queue_declare(queue='hello', durable=True) # 发送消息 channel.basic_publish(exchange='', routing_key='hello', body='Hello World!', properties=pika.BasicProperties(delivery_mode=2)) print(" [x] Sent 'Hello World!'") # 接收消息 def callback(ch, method, properties, body): print(" [x] Received %r" % body) ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_qos(prefetch_count=1) channel.basic_consume(queue='hello', on_message_callback=callback) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming() ``` 在这个示例中,我们设置了队列的 durable 属性为 True,表示队列需要被持久化。在发送消息时,我们设置了消息的 delivery mode 为 2,表示消息需要被持久化。在接收消息时,我们使用了确认机制,通过调用 ch.basic_ack() 方法确认消息已经被消费。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值