RabbitMQ如何保证消息的高可靠性

一、生产者投递消息保证不丢失

  • 为保证投递消息的吞吐量以及性能,不使用消息事务和同步发送,建议使用异步发送消息到队列
  • 在生产者发送消息到消息队列时,有可能发生以下三种情况:
  1. 生产者连接RabbitMQ失败
  2. 生产者投递消息到exchange失败
  3. exchange将消息转发给queue时失败
  • 针对以上三个问题,需要对投递消息失败做响应处理:
  1. try住发送消息的代码,处理相应异常
  2. 开启confirm机制,在confirm中处理返回结果
  3. 开启returnMessage机制,在returnedMessage中处理相应结果
# 开启confirm确认机制
spring.rabbitmq.publisher-confirms=true
# 开启returnMessage
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true
// 1. try住发送消息的代码,处理相应异常
	try {
		rabbitTemplate.convertAndSend("hello_fanout_exchange", "", context,correlationData);
	} catch (AmqpConnectException e) {
		log.info("发送失败");
		//在这里做消息发送失败的处理
		e.printStackTrace();
     }

//2. 开启confirm机制,在confirm中处理返回结果
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        log.info("ack:{}  cause:{}    CorrelationData:{}", ack, cause, JSON.toJSONString(correlationData));
    }

// 3. 开启returnMessage机制,在returnedMessage中处理相应结果
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.info("message:{}\n replyCode:{}\n  replyCode:{}\n     exchange:{}\n   routingKey:{}",
                JSON.toJSONString(message), replyCode, replyText, exchange, routingKey);
        String content = new String(message.getBody());
        log.info("content:{}",content);
    }

二、RabbitMQ保证不丢失

  • exchange和queue开启持久化
  • durable:是否持久化,在new Queue和Exchange对象时传入,默认为true,即开启持久化

三、消费者保证消息正确消费

  • 消费者开启手动ack确认机制,避免消息未正常消费,而该消息在消息队列中又被清除。
# 开启手动ack
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual
	@RabbitHandler
    public void process(Object o, Channel channel, Message message) {
        String hello = new String(message.getBody());
        log.info("Receiver:{}", hello);
        try {
        	//处理业务逻辑(需要保证幂等性)
            log.info("消息消费成功");
            //消息确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            log.info("消息消费失败");
        }
    }
  • 开启手动ack后,消息的状态有:ready和 unacked两种:
  1. ready:消息准备就绪,随时可以发送到消费者
  2. unacked:已经将消息发送给消费者,但是没有收到ack确认,这种状态的消息不会再次发送到消费者,直到连接改队列的消费者断开连接,才会将unacked的消息重置为ready。只需要消费者断开连接就会重置unacked的消息,不需要重新连接。
  • 消费者端处理逻辑建议:首先保证消息处理的幂等性,在成功处理业务逻辑后,手动回复ack;未成功处理业务逻辑就不要回ack了,包括也不要调用basicReject方法,调用basicReject会导致未unacked的消息从队列中移除。

四、消息类型的问题

  • 生产者和消费者的消息类型问题,建议使用json数据格式,即String类型。在消费者的代码里可以使用Message.getBody()获取消息体,也可以直接响应类型的参数类型接受数据,建议使用Message.getBody()获取消息内容,这样在rabbitmq的管理后台就可以直接发送消息,而不用设置header。

  • 消息重发机制

CREATE TABLE `rabbitmq_send_record` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `content` varchar(500) DEFAULT NULL COMMENT '消息内容',
  `exchange` varchar(30) DEFAULT NULL COMMENT '交换机名称',
  `queue` varchar(30) DEFAULT NULL COMMENT '队列名称',
  `routing_key` varchar(30) DEFAULT NULL COMMENT '路由key名称',
  `ack` tinyint(1) unsigned DEFAULT '0' COMMENT '发送消息ack确认标识,默认为false,需要confirm回调确认消息送达exchange。0:未发送成功 1:发送成功',
  `return_message` tinyint(1) unsigned DEFAULT '1' COMMENT 'exchange转发消息到队列,如果转发失败回调returnMessage方法,默认为true。0:转发失败 1:转发成功',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_ack` (`ack`) USING BTREE COMMENT '发送消息到exchange',
  KEY `idx_return_message` (`return_message`) USING BTREE COMMENT 'exchange转发消息到队列'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值