四种策略确保 RabbitMQ 消息发送可靠性!你用哪种?

如果能确认这两步,那么我们就可以认为消息发送成功了。

如果这两步中任一步骤出现问题,那么消息就没有成功送达,此时我们可能要通过重试等方式去重新发送消息,多次重试之后,如果消息还是不能到达,则可能就需要人工介入了。

经过上面的分析,我们可以确认,要确保消息成功发送,我们只需要做好三件事就可以了:

  1. 确认消息到达 Exchange。

  2. 确认消息到达 Queue。

  3. 开启定时任务,定时投递那些发送失败的消息。

2. RabbitMQ 的努力


上面提出的三个步骤,第三步需要我们自己实现,前两步 RabbitMQ 则有现成的解决方案。

如何确保消息成功到达 RabbitMQ?RabbitMQ 给出了两种方案:

  1. 开启事务机制

  2. 发送方确认机制

这是两种不同的方案,不可以同时开启,只能选择其中之一,如果两者同时开启,则会报如下错误:

我们分别来看。以下所有案例都在 Spring Boot 中展开,文末可以下载相关源码。

2.1 开启事务机制

Spring Boot 中开启 RabbitMQ 事务机制的方式如下:

首先需要先提供一个事务管理器,如下:

@Bean

RabbitTransactionManager transactionManager(ConnectionFactory connectionFactory) {

return new RabbitTransactionManager(connectionFactory);

}

接下来,在消息生产者上面做两件事:添加事务注解并设置通信信道为事务模式:

@Service

public class MsgService {

@Autowired

RabbitTemplate rabbitTemplate;

@Transactional

public void send() {

rabbitTemplate.setChannelTransacted(true);

rabbitTemplate.convertAndSend(RabbitConfig.JAVABOY_EXCHANGE_NAME,RabbitConfig.JAVABOY_QUEUE_NAME,“hello rabbitmq!”.getBytes());

int i = 1 / 0;

}

}

这里注意两点:

  1. 发送消息的方法上添加 @Transactional 注解标记事务。

  2. 调用 setChannelTransacted 方法设置为 true 开启事务模式。

这就 OK 了。

在上面的案例中,我们在结尾来了个 1/0 ,这在运行时必然抛出异常,我们可以尝试运行该方法,发现消息并未发送成功。

当我们开启事务模式之后,RabbitMQ 生产者发送消息会多出四个步骤:

  1. 客户端发出请求,将信道设置为事务模式。

  2. 服务端给出回复,同意将信道设置为事务模式。

  3. 客户端发送消息。

  4. 客户端提交事务。

  5. 服务端给出响应,确认事务提交。

上面的步骤,除了第三步是本来就有的,其他几个步骤都是平白无故多出来的。所以大家看到,事务模式其实效率有点低,这并非一个最佳解决方案。我们可以想想,什么项目会用到消息中间件?一般来说都是一些高并发的项目,这个时候并发性能尤为重要。

所以,RabbitMQ 还提供了发送方确认机制(publisher confirm)来确保消息发送成功,这种方式,性能要远远高于事务模式,一起来看下。

2.2 发送方确认机制

2.2.1 单条消息处理

首先我们移除刚刚关于事务的代码,然后在 application.properties 中配置开启消息发送方确认机制,如下:

spring.rabbitmq.publisher-confirm-type=correlated

spring.rabbitmq.publisher-returns=true

第一行是配置消息到达交换器的确认回调,第二行则是配置消息到达队列的回调。

第一行属性的配置有三个取值:

  1. none:表示禁用发布确认模式,默认即此。

  2. correlated:表示成功发布消息到交换器后会触发的回调方法。

  3. simple:类似 correlated,并且支持 waitForConfirms()waitForConfirmsOrDie() 方法的调用。

接下来我们要开启两个监听,具体配置如下:

@Configuration

public class RabbitConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {

public static final String JAVABOY_EXCHANGE_NAME = “javaboy_exchange_name”;

public static final String JAVABOY_QUEUE_NAME = “javaboy_queue_name”;

private static final Logger logger = LoggerFactory.getLogger(RabbitConfig.class);

@Autowired

RabbitTemplate rabbitTemplate;

@Bean

Queue queue() {

return new Queue(JAVABOY_QUEUE_NAME);

}

@Bean

DirectExchange directExchange() {

return new DirectExchange(JAVABOY_EXCHANGE_NAME);

}

@Bean

Binding binding() {

return BindingBuilder.bind(queue())

.to(directExchange())

.with(JAVABOY_QUEUE_NAME);

}

@PostConstruct

public void initRabbitTemplate() {

rabbitTemplate.setConfirmCallback(this);

rabbitTemplate.setReturnsCallback(this);

}

@Override

public void confirm(CorrelationData correlationData, boolean ack, String cause) {

if (ack) {

logger.info(“{}:消息成功到达交换器”,correlationData.getId());

}else{

logger.error(“{}:消息发送失败”, correlationData.getId());

}

}

@Override

public void returnedMessage(ReturnedMessage returned) {

logger.error(“{}:消息未成功路由到队列”,returned.getMessage().getMessageProperties().getMessageId());

}

}

关于这个配置类,我说如下几点:

  1. 定义配置类,实现 RabbitTemplate.ConfirmCallbackRabbitTemplate.ReturnsCallback 两个接口,这两个接口,前者的回调用来确定消息到达交换器,后者则会在消息路由到队列失败时被调用。

  2. 定义 initRabbitTemplate 方法并添加 @PostConstruct 注解,在该方法中为 rabbitTemplate 分别配置这两个 Callback。

这就可以了。

接下来我们对消息发送进行测试。

首先我们尝试将消息发送到一个不存在的交换机中,像下面这样:

rabbitTemplate.convertAndSend(“RabbitConfig.JAVABOY_EXCHANGE_NAME”,RabbitConfig.JAVABOY_QUEUE_NAME,“hello rabbitmq!”.getBytes(),new CorrelationData(UUID.randomUUID().toString()));

注意第一个参数是一个字符串,不是变量,这个交换器并不存在,此时控制台会报如下错误:

接下来我们给定一个真实存在的交换器,但是给一个不存在的队列,像下面这样:

rabbitTemplate.convertAndSend(RabbitConfig.JAVABOY_EXCHANGE_NAME,“RabbitConfig.JAVABOY_QUEUE_NAME”,“hello rabbitmq!”.getBytes(),new CorrelationData(UUID.randomUUID().toString()));

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

如何快速更新自己的技术积累?

  • 在现有的项目里,深挖技术,比如用到netty可以把相关底层代码和要点都看起来。
  • 如果不知道目前的努力方向,就看自己的领导或公司里技术强的人在学什么。
  • 知道努力方向后不知道该怎么学,就到处去找相关资料然后练习。
  • 学习以后不知道有没有学成,则可以通过面试去检验。

我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!

以上面试专题的答小编案整理成面试文档了,文档里有答案详解,以及其他一些大厂面试题目

八年CRUD,疫情备战三个月,三面头条、四面阿里拿offer面经分享

八年CRUD,疫情备战三个月,三面头条、四面阿里拿offer面经分享

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
么学,就到处去找相关资料然后练习。

  • 学习以后不知道有没有学成,则可以通过面试去检验。

我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!

以上面试专题的答小编案整理成面试文档了,文档里有答案详解,以及其他一些大厂面试题目

[外链图片转存中…(img-qr3rY2T2-1712415642600)]

[外链图片转存中…(img-Y2TFs646-1712415642601)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值