RabbitMQ最佳实践

本文详细探讨了RabbitMQ在确保消息不丢失、正确投递和业务一致性方面的方法,包括消息持久化、发送确认、手动消费确认、设置死信交换和队列管理等方面。通过设置和策略,确保消息的可靠性和系统的稳定性。
摘要由CSDN通过智能技术生成

 

在使用消息机制时,我们通常需要考虑以下几个问题:

  • 消息不能丢失
  • 保证消息一定能投递到目的地
  • 保证业务处理和消息发送/消费的一致性

本文以RabbitMQ为例,讨论如何解决以上问题。

消息持久化

如果希望RabbitMQ重启之后消息不丢失,那么需要对以下3种实体均配置持久化:

  • exchange
  • queue
  • message

声明exchange时设置持久化(durable = true)并且不自动删除(autoDelete = false):

boolean durable = true;
boolean autoDelete = false;
channel.exchangeDeclare("dlx", TOPIC, durable, autoDelete, null)

声明queue时设置持久化(durable = true)并且不自动删除(autoDelete = false):

boolean durable = true;
boolean autoDelete = false;
channel.queueDeclare("order-summary-queue", durable, false, autoDelete, queueArguments);

发送消息时通过设置deliveryMode=2持久化消息:

AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                    .contentType("application/json")
                    .deliveryMode(2)
                    .priority(0)
                    .build();
channel.basicPublish("order", "order.created", false, properties, "sample-data".getBytes())

发送确认

有时,业务处理成功,消息也发了,但是我们并不知道消息是否成功到达了rabbitmq,如果由于网络等原因导致业务成功而消息发送失败,那么发送方将出现不一致的问题,此时可以使用rabbitmq的发送确认功能,即要求rabbitmq显式告知我们消息是否已成功发送。

首先需要在channel上设置ConfirmListener:

channel.addConfirmListener(new ConfirmListener() {
                public void handleAck(long seqNo, boolean multiple) {
                    if (multiple) {
                        logger.info(seqNo + "号及其以前的所有消息发送成功,当消息发送成功后执行相应逻辑,比如标记事件为已发送或者删除原来事件");
                    } else {
                        logger.info(seqNo + "号发送成功,当消息发送成功后执行相应逻辑,比如标记事件为已发送或者删除原来事件");
                    }
                }

                public void handleNack(long seqNo, boolean multiple) {
                    if (multiple) {
                        logger.info(seqNo + "号及其以前的所有消息发送失败,当消息发送失败后执行相应逻辑,比如重试或者标记事件发送失败");
                    } else {
                        logger.info(seqNo + "号发送失败,当消息发送失败后执行相应逻辑,比如重试或者标记事件发送失败");

                    }
                }
            });

然后在发送消息直线需要开启发送确认模式:

//开启发送者确认
channel.confirmSelect();

然后发送消息:

channel.basicPublish("order", "order.created", false, properties, "sample-data".getBytes());

当消息正常投递时,rabbitmq客户端将异步调用handleAck()表示消息已经成功投递,此时程序可以自行处理投递成功之后的逻辑,比如在数据库中将消息设置为已发送。当消息投递出现异常时,handleNack()将被调用。

通常来讲,发送端只需要保证消息能够发送到exchange即可,而无需关注消息是否被正确地投递到了某个queue,这个是rabbitmq和消息的接收方需要考虑的事情。基于此,如果rabbitmq找不到任何需要投递的queue,那么rabbitmq依然会ack给发送方,此时发送方可以认为消息已经正确投递,而不好用关系消息没有queue接收的问题。但是,对于rabbitmq而言,这种消息是需要记录下来的,否则rabbitmq将直接丢弃该消息。此时可以为exchange设置alternate-exchange,即表示rabbitmq将把无法投递到任何queue的消息发送到alternate-exchange指定的exchange中,通常来说可以设置一个死信交换(DLX)。

事实上,对于exchange存在但是却找不到任何接收queue时,如果发送是设置了mandatory=true,那么在消息被ack前将return给客户端,此时客户端可以创建一个ReturnListener用于接收返回的消息:

channel.addReturnListener(new ReturnListener() {
                @Override
                public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    logger.warn("消息无法正确投递,已返回。");
                }
            });

但是需要注意的是,在return之后,消息依然会被ack而不是nack,还不如不设置madatory呢,因此return有时并不见得有用。

需要注意的是,在发送消息时如果exchange不存在&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值