RocketMQ 重试机制的概念与最佳实践|学习笔记

简介: 快速学习 RocketMQ 重试机制的概念与最佳实践

开发者学堂课程消息队列 RocketMQ 5.0 云原生架构升级课程RocketMQ 重试机制的概念与最佳实践学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/1234/detail/18397


RocketMQ5.0 重试机制的概念与最佳实践

 

内容介绍

一、课前介绍

二、RocketMQ5.0 重试机制概念

三、生产者发送消息时重试介绍

四、消费者重试处理的模式与案列分享

五、课堂总结

 

一、课前介绍

分享内容是Rocket MQ中的重试机制的概念与最佳实践,今天的分享一共分为以下4个部分,第一部分是Rocket MQ中与重试的概念,第二部分是生产者发送消息时重试的条件,具体行为与最佳实践,第三部分是如果在RocketMQ新的5.0的版本中引入了两种新的消费者类型,分别是putsch consumer和simple consumer,讲解这两种消费者消费重试的场景功能约束和建议,第四部分是今天的总结

 

二、RocketMQ5.0 重试机制概念

(1)重试机制介绍,在消费者进行消费的时候,因为网络因素或因为消费者返回了消费失败等情况,此时产生消费城市,为了简化今天的主题,仅讨论第一种跟第三种,也就是发送重试跟消费重试两种情况。

image.png

需要重试机制是分布式系统中网络和节点都是不可靠,需要通过一定的重试机制来进行容错,实现最终一致性,帮助构建弹性更高可用的系统,在如Rocket MQ的生产者发送消息到服务端时,生产者内置了请求重试的逻辑,支持在初始化的时候就配置这个消息发送的最大尝试次数,默认是两次加上第一次发送,所以一共是三次。

失败的时候会按照设置的重庆次次数进行重新发送,直到消息发送成功到服务端,或者是最后一次还是收到服务端调用失败的一个响应,并且返还给客户端的调用线程,无论是同步发送还是异步发送,其实都是支持重试机制的,它们的区别是在同步发送的时候,调用线程会一直阻塞,直到获取最终的结果,在异步发送的时候,发送的结果是通过回调函数执行的,最终结果通过异常事件或成功事件返回,在Rocket MQ的不同场景下,重试的行为是不一致的。

2)RocketMQ不同场景下的重制行为

这里首先来介绍一下什么是服务端流控服务端流控指的是当服务端压力较大时,将限制部分生产者生产消息,也就是说这部分生产者生产的消息被服务端拒绝了,此时把它称为服务端流控。

 

对于非流控场景触发之后会立刻重试这时候触发的条件,比如说是网络抖动,这个时候生产者立刻重试就可以进行发送成功了。对于服务端流控错误导致的时候,系统会按照指数退避策略进行重试,那么为什么要引入这个复杂的退避和随机抖动的这种机制,其实是当请求被服务端限流时,通常不希望立刻重试,以便请求立刻淹没了服务器,因为你越重试服务端累积的请求就越多,此时服务端的资源开销就越大,你反而会导致这个服务端拒绝更多的请求,导致整体服务的雪崩,所以选择了进行指数退避,加上一个随机走动的形式,控制这个重试的行为。

 

三、生产者发送消息时重试的介绍

1)重试行为的最佳实践

下面是一个建议的算法,其中这个退避时间和退避因子都是可以支持默认配置的,就是可以支持自定义的配置。Look at mq发送者重试处理的一个最佳实践,重试也不是万能的,重试会带来一些副作用。比如说主要包括两方面,一方面是消息重复,服务端的压力增大,因为远程的调用的不确定性,分布式有好几种结果,就是成功失败或未知因请求超时触发的消息重试流程。此时客户端没有办法知道这个请求在服务端的处理结果,所以当此这个时候重试可能会到造成原来的一条消息已经成功了,这个时候生产者又重新将接到消息发送了一遍,导致消费方收到了两条重复的消息,这个时候消费者应该按照用户的ID或者是业务组件对这个消息进行一个幂等的处理,此外如果无限的重试的话,也会造成这个服务端的处理压力更增大,那对用户来说,用户应该怎么做才是一个最好的最佳实践。

主要包括以下4个点,第一点就是合理设置1个发送的超时时间,发送的最大次数,发送的最大次数在客户端初始化的时候用client configuration进行配置,对于某些实时调用的场景来说,可能会因为发消息发送链路被阻塞,导致整体的耗时比较高,比如说重试了好几三次,然后每次是三秒,此时整体的消费好,发送的耗时就可能高达9秒,这时候你对用户的体验是比较差的,所以需要合理评估每次调用的请求超时时间和最大重试次数,避免影响全链路的耗时。

第二种情况是保证消息发送的不丢失,那么因为分布式环境的复杂性,比如说客户端到就是生产者到的服务端之间,如果网络是不可达的,此时无论怎么重试发送一定是失败的,在这种情况下,有两种兜底策略,第一种是向用户直接返回失败,这种一般适用于在线业务。

如果你是一种一步的离线任务的处理,那么就将这条消息写入数据库进行兜底尝试由后台线程定时的重试,这样的话也可以保证业务逻辑的最终一致性。第三点是发送方要关注是否是有流控异常,导致无法重试。在流控异常的根本原因其实是系统容量不足,如果遇到容量不足的场景,则建议执行服务端扩容,或者是临时将这个请求调用到其他系统进行处理,就是走其他的逻辑。

第四个请最佳实践其实就是是针对于旧版本的旧版本,那么如何使用故障仪式机制,以上讲的部分策略是在新的5.0版本中才有的,在柔克mq的4点几的版本以下的时候,有一个 策略就是一种故障延迟机制,可以在客户端produce点线的 latency fought in able这个开关进行开启和配置相关的次数。

 

四、消费者重试处理的模式与案例分享

(1)Rocket MQ消费重试处理

Rocket MQ生产者的重试处理,接下来看一下如果Rocket MQ消费者的重试处理流程,那么消息中间件在做异不解耦的一个典型问题,就是下游服务如果处理消息失败的时候,那么应该怎么做,有必要了解一下这个消费者若干Rocket MQ的确认机制以及消费重试的策略,这可以帮助解决两以下两个问题。

第一个点是告诉如何保证业务完整的处理消息,避免消费就是消息消费异常的状态导致的业务状态不一致。

第二种情况是的业务异常,应用异常时,就是比如说处理一个的应用,因为档期啦然后就导导致数据不一致,此时处理中的消息状态应该如何恢复,消费重试的具体行为又是怎么样的,在回答这些问题之前,首先要引入一些概念,就是右边的5个点,首先是什么时候认为消费是失败的,消费者在接收到消息后,将调用用户重写的这个消费函数执行业务逻辑,如果消费端返回了这个 Field,或者是v4版本中的consumer latter,抛出了非预期的一场或者是消费处理超时,只要服务端一定时间内没有收到这个客户端成功的响应,都将认为是消费失败。

那么消费重试又是指什么,消费者在消费某条消息失败后,服务端会根据这个内置的重试策略重新向客户端投递该消息,如果超过一定次数之后还没有消费成功,则这条消息不会再继续重试,它会将会标记并且发送到死期队列中。当然用户也可以通过控制台或者是查询队列,查询这些消息,那重试过程的状态机其实是指消息的一条消息的生命周期中关于重试流程的状态和变化逻辑,重试的间隔是指当上一次的重试消费失败或者超时之后,服务端下一次重新投递的这个时间间隔,最大的重试次数指的是这条消息可被消费的最大次数,当然Rocket MQ,因为最少一次的消费模型,所以最大重试次数可能大于用户的配置。

(2)Rocket MQ中消费重试的场景与案例

Rocket MQ中消费重试的场景与案例,需要注意的是重试是用来应对异常情况的,给予程序再次消费失败和消费消失败消息的机会,他们不应该被用作常态化的链路。

如果Rocket MQ推荐的场景,比如说对于消费失败的场景进行重试的,有比如说是缓存和数据库,就是当的消费逻辑中有更新缓存或更新数据库的逻辑,此时产生了一些失败,这个时候预期一段时间偶尔执行就会成功,这样的时候是适合使用重试机制的。

第二,这种适合用来重试的消息一定是一个小概率事件,对于大批的消息并且只有很少量的失败,此时应该去排查第三方应用的问题。比如接下来举一个正面的案例,例如消有一条消息的消费逻辑是扣减库存,极少量的商品因为版本冲突导致扣减失败,而重试立刻重试的话一般就会立刻成功,此时使用重试逻辑是很合适的,注意此时的冲突一定是要比较小的。

那么典型的错误的使用场景就如右边所示,消费处理逻辑中,使用消费失败来做条件判断,让结果分流是不合理的。返一个返利,就比如说订单在数据库中的状态是已经取消了,此时如果用来收到比如说要发货的消息处理的时候用来不应该返回发送失败,而应该返回这条消息是消费成功了,但是将数据在数据库中标记这条消息是不用发货的。第二点是也就是在生产实践中一个常见的错误用法,就是使用消费重试来进行一个消费的限流,这是很不合理的。

所谓的限流是指将超出消费者消费能力的流量暂时的堆积在队列中,以达到削峰的作用,而不是让这些消息进进入一个城市的链路。这种如果这么做了,会产生一些更多的系统开销主要包括以下方面,第一个点是Rocket MQ中的内部重试的涉及到写放大,大量的重试将带来严重的io压力。

然后第二点是从有复杂的退避逻辑,内部实现为TV定时器,这种虫式将导致大量的延迟消息,大量的重试消息延后消费及削峰的周期被大幅度延长。

image.png

左图中展示了一个削峰限流的场景,在如果mq消费逻辑出现不可用的时候,选择了限流,而非立即重试上述误用的场景其实是组合了限流和重试能力来进行削峰,而Rocket MQ推荐的削峰最佳手段为限流和堆积,业务以保障自身应用的状态为前提,需要对消费的流量进行限流,并利用Rocket MQ的堆积能力,将超出当前处理能力的消息滞后消费以达到削峰的目的。

(3)消费者单机限流演示

image.png

接下来演示如何进行消费者单机限流,这边给的是两个版本的客户端,首先利用Ridley metre进行一个声明,比如说声明了一个单节点的限制,在消费逻辑中阻塞,直到获得一个令牌,或者设置一就是跨二的超时时间,这样就可以保证消费的线程不长时间阻塞在获取令牌上。

右边是如果Rocket MQ5.0的一个新版本的push consumer,在消费逻辑中也是先用使用获取机制进行获取一个练排,实现了一个单机的限流,如果你有一些分布式的解决限流,更好的手段,比如说像sentence等。再Rocket MQ的Pushconsumer中消费的重试策略,这里首先来介绍一下消费从重试,也就意味着消息有几种状态。消息一共有,目前在消费的流程中一共有4种状态,分别是ready表示已就绪状态,表示消息在消息队列的服务端已就绪,可以被消费者消费in flight,也就是飞行中处理中的一个概念。

4)Rocket MQ的Pushconsumer中消费的重试策略

image.png

消息被消费者获取到,或者还在这个网络中传输,处于消费中,但是还没有返回消费结果的一个状态。

Commit提交状态。其其实肯贝特表示的是这条消息已经成功的被消费了,但并不知道它是具体的是消费成功还是消费失败了。迪奥q表示一个死性状态,dead letter,表示消费逻辑的最终兜底机制。如果消息一直处理失败,并且不断进行重试的时候,达到最大重视次数还没有成功,此时消息不会再进行重试,会被投递到死因队列。

那么几种状态的一个转化的状态机就如左边所示,以旧式的消息通被客户端获取到,他的状态进入处理中处理中,如果成功失败成功,它就向服务端反馈成功,失败它就会向服务端反馈失败。如果一条消息重试了很多次还没有成功的话,达到最大层次次数,它就会进入死线。重试是使用一种退避机制进行重试的,第一次重试和第二次重试之间的间隔是10秒,uh第二次重试和第三次重试之间的间隔其实是30秒,通过不断的加大重试机制,可以使得重试的流量被控制在一定范围内。当然每一次重试的时间间隔是越来越长的,对于顺序消息来说,重试的间隔为固定三秒,而非上述所说的这个退避重视策略。

那么为什么对于顺序消息来说,后一条消息必须等前,就同一个队列中的后一条消息,必须等前一条消息消费完成才可以进行消费,如果前一条消息处理到一半,此时这条它的状态是味觉的,这个时候消费下一条消息其实会破坏这个消费的顺序性,所以它的策略是为固定时间,直到上一条消息明确的返回成功,或者是进入死性队列之后,他才会消费下一条消息。

(5)Rocket MQ的simple consumer的消费重试策略

 image.png

接下来,Rocket MQ的simple consumer的消费重试策略是consumer的消费城市策略不同,simple consumer的消费者的消费城市间隔是预分配的,每次消费者或在调用API的时候会设置一个不可见时间,因为这波duration及消息的最大处理时长如下,图所示一开始消息在服务端的状态是已就绪,服客户端请求了一个5秒钟的这个不可见时间,也就是预估的消费的最大耗时,此时消息在服务端进入一个不可见的状态,如果在这5秒内客户端没有明确的返回消费成功,那么也就是说消费者消费到一半或者是排队或者是返回了消费失败,其实对服务端来说都是认为它是消费失败的。如果此时就此时消息消费失败,将触发重试客户端,不需要设置下一次的重试间隔客户端,下一次重试拉取的时候,这条消息又将被拉取到客户端进行一个重新处理。

在simple consumer由于不可见时间是预分配的,可能和实际业务中处理的时间差别较大,所以可以通过这个 API修改这个不可见时间,比如说预先觉得这个消费的处理耗时是20毫秒,但20毫秒可能消费者处理不完,那么此时就可以修改这个不可见时间,延长消息的处理时间,避免消息的重试机触发了这个重试机制,修改消息的不可见时间需要满足两个条件,第一个条件是消息处理有没有超时,第二个条件是消息处理还没有提交这个消费者状态,也就是说服务端还认为这条消息在消费中如下图所示,消息在不可见的时间修改后立刻生效,直接调用API开始时候重新计算这个不可见时间。那么当服务端在认为这条消息在处理到一半的时候,客户端又给这条消息增加了一个不可见时间,对于在客户端视角看,它相当于给这条消息延长了一个处理时间,

接下来看一个案例,某产品使用消息队列来进行一个发送和接收的一个视频渲染业务逻辑的解耦,首先发送方会发送这个视频任务的一个编号,消费者在接收到这个编号后就进行任务处理,由于这种视频渲染或者是解码这种逻辑,业务逻辑的耗时是比较长的,一般是几分钟甚至可能达到几个小时。消费者在重新消费到同一个任务的时候,因为这个消费任务没有完成,在低版本的API中只能返回消费失败,在这种全新的APA下,用户可以通过修改这个不可见时间来给消息进行一个续期,实现对单条消息状态的一个精确控制。

那么怎么去修改,这个不可见时间就是通过simple consumers Chang'e invisible的同步接口和change invisible duration性的一个异步接口来实现消息的重试间隔,等于不可见时间减去消息的实际处理时长。

举个例子来说,消息的不可见时间设置为30毫秒,实际10秒钟就返回了失败,此时距离下次重试还需要20毫秒,但是这个时候客户端可能想立刻重试,所以他可以将这个不可见时间change一下改为20毫秒。直到30毫秒的时候,消息如果还没有处理完成,还没有返回结果,那么消费超时就会立刻重试Rocket MQ中消费者重试的功能约束和最佳实践,第一点是设置消费的最大超时时间和超重试次数,尽快明确的向服务端返回这个成功或失败,不要以超时有时候是消费逻辑异常代替了这个消费失败。

第二点是合理的使用重试功能,一个错误的事例是当前的消费速度过高,触发限流的时候,此时返回消费失败,等待下次重新消费,这个原因刚才已经讲过了,是不不断加大了服务端和客户端之间的通信压力,浪费了资源,还会导致消费消费的一个顺序性的破坏。正确的事例应该是使用单击或者是分布式的限流组件进行限流,延迟获取消息,稍后再进行消费。

第三点是当发送城市和消费城市会导致时候会导致一条就是重复这单条消息,重复的被一个客户端获取到,消费方应该有一个良好的设计,正确的一个事例是某系统中消息的一个功能是给用户发送送一个短信,那么当这个短信已经发送成功的时候,消费者在接收到这个信息,此时就应该直接返回消费成功了。

 

五、课堂总结

今天主要介绍了一下消费重试和发送重试的一个基本概念,生产者消费者收发消息时触发条重试的条件和具体行为,以及Rocket MQ收发容错的一个最佳实践。其实重试策略是帮助从随机的短暂的故障中恢复过来的,是在容忍错误提高可用性的一种强大机制,但请记得重试对于分布式系统来说其实是一种自私的行,为因为客户端认为这个请求很重要,所以他才会重试他要求服务端花费更多的资源来处理这个请求,盲目的设计,重试设计是不可取的,合理的使用重试可以帮助构建更加弹性且可靠的系统。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
3月前
|
消息中间件 中间件 数据安全/隐私保护
RabbitMQ 的核心概念
RabbitMQ 的核心概念
27 2
|
1天前
|
消息中间件 存储 网络协议
消息中间件RabbitMQ---概述和概念 【一】
该文章提供了对消息中间件RabbitMQ的全面概述,包括其核心概念、工作原理以及与AMQP和JMS的关系。
消息中间件RabbitMQ---概述和概念 【一】
|
5天前
|
消息中间件 RocketMQ
RocketMQ - 生产者最佳实践总结
RocketMQ - 生产者最佳实践总结
11 0
|
1月前
|
消息中间件 负载均衡 算法
【RocketMQ系列十二】RocketMQ集群核心概念之主从复制&生产者负载均衡策略&消费者负载均衡策略
【RocketMQ系列十二】RocketMQ集群核心概念之主从复制&生产者负载均衡策略&消费者负载均衡策略
49 2
|
1月前
|
消息中间件 NoSQL 关系型数据库
【RocketMQ系列十三】RocketMQ的集群核心概念之消费重试&死信队列&幂等消息的出现以及处理
【RocketMQ系列十三】RocketMQ的集群核心概念之消费重试&死信队列&幂等消息的出现以及处理
43 1
|
1月前
|
消息中间件 存储 RocketMQ
【RocketMQ系列十】RocketMQ的核心概念说明
【RocketMQ系列十】RocketMQ的核心概念说明
28 1
|
2月前
|
消息中间件 Arthas 监控
消息队列 MQ产品使用合集之每次重置reconsumeTimes就无法达到死信阈值,重试次数是否就要应用方控制
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
消息队列 MQ产品使用合集之每次重置reconsumeTimes就无法达到死信阈值,重试次数是否就要应用方控制
|
2月前
|
消息中间件 存储 物联网
RocketMQ基础概念
RocketMQ基础概念
34 1
|
3月前
|
消息中间件 存储 Apache
RocketMQ实战教程之常见概念和模型
Apache RocketMQ 实战教程介绍了其核心概念和模型。消息是基本的数据传输单元,主题是消息的分类容器,支持字节、数字和短划线命名,最长64个字符。消息类型包括普通、顺序、事务和定时/延时消息。消息队列是实际存储和传输消息的容器,是主题的分区。消费者分组是一组行为一致的消费者的逻辑集合,也有命名限制。此外,文档还提到了一些使用约束和建议,如主题和消费者组名的命名规则,消息大小限制,请求超时时间等。RocketMQ 提供了多种消息模型,包括发布/订阅模型,有助于理解和优化消息处理。
|
2月前
|
消息中间件 存储 中间件
【主流技术】聊一聊消息队列 RocketMQ 的基本结构与概念
2.6Broker 代理服务器(Broker)是消息中转角色,负责存储消息、转发消息。代理服务器在 RocketMQ 系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。代理服务器也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等。 2.7Pull Consumer 拉取式消费(Pull Consumer)是 Consumer 消费的一种类型,也是默认的类型。下游应用系统通常主动调用 Consumer 的拉消息方法从 Broke r服务器拉消息,即主动权由下游应用控制。一旦获取了批量消息,应用就会启动消费过程。