【易客吧】_全网激活码总代_激活码商城

您现在的位置是:首页 > 热门资讯 > 正文

热门资讯

死信队列和延时队列 (死信队列和延迟队列的理解)

用户投稿2024-03-30热门资讯22

死信队列和延时队列是在消息队列系统中常见的概念,它们在保证消息可靠性和系统稳定性方面发挥着重要的作用。本文将分别深入探讨死信队列和延时队列,解释它们的作用、优势以及应用场景。

死信队列

死信队列(Dead Letter Queue,DLQ)是一种用来存放无法被消费者正确处理的消息的队列。当一个消息无法被消费者消费,可能是因为消息格式不正确、消费者处理能力不足等原因,这时系统会将这些无法正常处理的消息转移到死信队列中进行存放,以便后续进一步分析处理。

死信队列的作用在于保证消息的可靠性和完整性。通过死信队列,系统能够捕获那些无法被正常消费的消息,避免消息丢失或被错误处理,保障系统数据的完整性。同时,死信队列也能帮助系统进行故障排查和问题定位,提高系统的稳定性和可维护性。

在实际应用中,死信队列常见于消息队列系统中,如RabbitMQ、Kafka等。通过配置死信队列,可以对异常消息进行有效管理和处理,提高系统的容错能力。

延时队列

延时队列(Delay Queue)是一种可以延迟消息投递的队列,即消息发送到队列后,并不立即被消费者消费,而是在一定的延迟时间之后才会被投递给消费者。延时队列的设计初衷是为了满足一些需要延迟处理的业务场景,如订单超时未支付自动取消等。

延时队列的优势在于能够简化业务逻辑,提高系统的可靠性和稳定性。通过将延迟处理逻辑放入延时队列中,可以避免复杂的定时任务调度和管理,减少人工干预,降低系统出错的风险。

在实际应用中,延时队列经常用于处理一些需要延迟执行的业务逻辑,如定时任务调度、消息提醒通知等。多种消息队列系统都提供了延时队列的支持,开发人员可以根据业务需求选择合适的队列类型。

结语

死信队列和延时队列在消息队列系统中具有重要意义。死信队列保障了消息的完整性和可靠性,帮助系统处理异常情况;而延时队列简化了业务处理流程,提高了系统的稳定性和可维护性。开发人员应当根据实际需求选择合适的队列类型,并合理配置队列参数,以提升系统的性能和效率。


延时队列常用实现详解

队列是一种线性表,内部的元素是有序的,具有先进先出的特性。 延时队列,顾名思义,它是一个队列,但更重要的是具有延时的特性,与普通队列的先进先出不同,延时队列可以指定队列中的消息在某个时间点被消费。

DelayQueue是无界的延时阻塞队列,内部是使用优先级队列PriorityQueue实现的,其是按时间来定优先级的延时阻塞队列,只有在延迟期满时才能从队列中提取元素,先过期的元素会在队首,每次从队列里取出来都是最先要过期的元素, 当执行队列take操作元素未过期时会阻塞当前线程到元素过期为止 ;PriorityQueue是通过二叉小顶堆实现, 其任意一个非叶子节点的权值,都不大于其左右子节点的权值。

示例 队列中的元素必须实现Delayed接口

redis key的过期事件是通过redis 2.8.0之后版本提供的订阅发布功能(pub/sub)下发的,当key过期后系统自动Pub,应用程序只需订阅(sub)该事件即可。

实现步骤

示例

存在的问题

key的失效通知无法保证时效性。redis过期策略有一下三种:

默认情况下,Redis 使用的是 惰性删除 + 定期删除 的策略;每隔一段时间(可通过hz参数设置每秒执行的次数),Redis 会分别从各个库随机选取部分测试设置了过期时间的 Key,判断它们是否过期,过期则删除;如果 key 已过期,但没有被定期删除,由于惰性删除策略,在下次请求获取该数据时会将该数据删除。

可通过如下方式提高时效性

redis zset 结构是一个有序集合,每个元素都会关联一个 double 类型的分数,通过分数来为集合中的成员进行从小到大的排序;有序集合的成员是唯一的,但分数(score)却可以重复。

实现思路

将任务id作为member,到期时间作为score存入到zset中,然后不断轮询获取第一个元素,判断其是否过期,过期后删除并执行任务即可。

也可以通过lua脚本将 zrangebyscore 和 zrem 操作变成原子操作,避免了多线程时同一个me mber多次zrem。

存在的问题

RabbitMQ本身没有直接支持延迟队列功能,但是可以通过ttl及dlx(Dead Letter Exchanges)特性模拟出延迟队列的功能。

绑定在死信交换机上的队列。RabbitMQ的Queue(队列)可以配置两个参数x-dead-letter-exchange(死信交换机)和x-dead-letter-routing-key(指定routing-key发送,可选),当消息在一个队列中变成死信(dead message)之后,按照这两个参数可以将消息重新路由到另一个DLX Exchange(死信交换机),让消息重新被消费。

队列出现Dead Letter的情况有:

RabbitMQ可以对消息和队列设置TTL,为队列设置时,队列中所有消息都有相同的过期时间;对消息进行单独设置,每条消息过期时间可以不同;如果同时设置了队列的ttl和消息的ttl以两者之间TTL较小的那个数值为准。消息超过设置的ttl值未被消费,将会变为死信,消费者将无法再收到该消息。

ttl消息按照入发送顺序排列在队列中,且rabbitMQ只会判断队列头消息是否失效,失效后才会加入到死信队列中,如果发送多个过期时间不一致的消息,有可能后面的消息已经过期了,但队列头消息没有过期,导致其他消息不能及时加入到死信队列被消费。

针对上述的问题,可以使用 rabbitmq_delayed_message_exchang 插件来解决。

安装该插件后会生成新的Exchange类型 x-delayed-message ,该类型消息支持延迟投递机制,接收到消息后并未立即将消息投递至目标队列中,而是存储在 mnesia (一个分布式数据系统)表中,检测消息延迟时间(通过消息头的x-delay指定),如达到可投递时间时并将其通过 x-delayed-type 类型标记的交换机类型投递至目标队列。

插件的安装

使用示例

插件的局限

时间轮的应用广泛,包括linux内核的调度、zookeeper、netty、kafka、xxl-job、quartz等均有使用时间轮。

图中的圆盘可以看作是钟表的刻度。比如一圈round长度为24秒,刻度数为8,那么每一个刻度表示3秒。那么时间精度就是3秒。每个刻度为一个bucket(实际上就是TimerTaskList),TimerTaskList是环形双向链表,在其中链表项TimeTaskEntry封装了真正的定时任务TimerTask。TimerTaskList使用expiration字段记录了整个TimerTaskList的超时时间。TimeTaskEntry中的expirationMs字段记录了超时时间戳,timerTask字段指向了对应的TimerTask任务;根据每个TimerTaskEntry的过期时间和当前时间轮的时间,选择一个合适的bucket,把这个TimerTaskEntry对象放进去;对于延迟超过时间轮所能表示的范围有两种处理方式,一是通过增加一个字段-轮数,Netty 就是这样实现的;二是多层时间轮,Kakfa 是这样实现的。

下面介绍下kafka的多层时间轮,层数越高时间跨度越大。

每个使用到的TimerTaskList都会加入到DelayQueue中,DelayQueue会根据TimerTaskList对应的超时时间expiration来排序,最短expiration的TimerTaskList会被排在DelayQueue的队头,通过一个线程获取到DelayQueue中的超时的任务列表TimerTaskList之后,既可以根据TimerTaskList的expiration来推进时间轮的时间,也可以就获取到的TimerTaskList执行相应的操作,TimerTaskEntry该执行过期操作的就执行过期操作,该降级时间轮的就降级时间轮。

假设现在有一个任务在445ms后执行,默认情况下,各个层级的时间轮的时间格个数为20,第一层时间轮每一个时间格跨度为1ms,整个时间轮跨度为20ms,跨度不够。第二层时间轮每一个时间格跨度为20ms,整个时间轮跨度为400ms,跨度依然不够,第三层时间轮每一个时间格跨度为400ms,整个时间轮跨度为8000ms,现在跨度够了,此任务就放在第三层时间轮的第一个时间格对应的TimerTaskList,等待被执行,此TimerTaskList到期时间是400ms,随着时间的流逝,当此TimerTaskList到期时,距离该任务到期时间还有45ms,不能执行该任务,将重新提交到时间轮,此时第一层时间轮跨度依然不够,不能执行任务,第二层时间轮时间格跨度为20,整个世间轮跨度为400,跨度足够,放在第三个时间格等待执行,如此往复几次,高层时间轮最终会慢慢移动到低层时间轮上,最终任务到期执行。

RocketMQ:死信队列和消息幂等

上一篇 《RocketMQ:消息重试》 中我们提到当一条消息消费失败时,RocketMQ会进行一定次数的重试。 重试的结果也很简单,无非就是在第N次重试时,被成功消费。 或者就是经过M次重试后,仍然没有被消息。 这通常是由于消费者在正常情况下无法正确地消费该消息。 此时,RocketMQ不会立即将消息丢弃,而是将其发送到该消费者对应的特殊队列中去。 在RocketMQ中,这种正常情况下无法被消费的消息被称为死信消息(Dead-Letter Message),存储死信消息的特殊队列称为死信队列(Dead-Letter Queue)。 (1)死信消息具有以下特性: (2)死信队列具有以下特性: (1)在控制条查询出现死信队列的主题信息 (2)在消费界面根据主题查询死信消息 (3)选择重新发送消息 一条消息进入死信队列,意味着某些因素导致消费者无法正常消费该消息。 因此,通常需要我们对其进行特殊处理。 排查可疑因素并解决问题后,可以在消息队列 RocketMQ 控制台重新发送该消息,让消费者重新消费一次。 RocketMQ并不保证一条消息只会被推送一次,因此一条消息就有可能被消费多次。 消费者在接收到消息以后,有必要根据业务上的唯一 Key 对消息做幂等处理的必要性。 在互联网应用中,尤其在网络不稳定的情况下,RocketMQ 的消息有可能会出现重复,这个重复简单可以概括为以下情况: 因为 Message ID 有可能出现冲突(重复)的情况,所以真正安全的幂等处理,不建议以 Message ID 作为处理依据。 最好的方式是以业务唯一标识作为幂等处理的关键依据,而业务的唯一标识可以通过消息 Key 进行设置: 消费方收到消息时可以根据消息的 Key 进行幂等处理:

死信队列和延时队列 (死信队列和延迟队列的理解) 第1张

RabbitMQ ACK、NACK、Type、TTL、死信

起因:在实际项目开发过程中,需要使用RabbitMQ来实现消息队列的功能,比如说我发布一个动态之后,需要在30分钟使用默认用户给他点几个赞,之前考虑使用redis的zset对他进行操作,之后决定使用RabbitMQ,专业的事情使用专业的工具来操作。

什么是TTL:Time To Live ,也就是生存时间

首先要看一下什么是死信:当一个消息 无法被消费 时就变成了死信

死信是怎么形成的:消息在没有被消费前就失效的就属于死信

一个系统中是没有无缘无故生成的消息,如果这个消息失效了没有了,是不是可能导致业务损失,如果这种消息我们需要记录或补偿,将这种消息失效的时候放到一个队列中,待我们人工补偿和消费,这个放死信的队列就是死信队列

希望我们的消息在失效的时候进入到死信队列中

我们的死信队列其实也是一个正常队列,只是赋予了他这个概念

死信队列的另一功能就是延迟消息

x-dead-letter-exchange :这个参数就是指定死信队列的Exchange的名字,这个Exchange就是一个普通的Exchange,需要手工创建

x-dead-letter-routing-key :这个参数就是指定死信队列的Routingkey,这个routingkey需要自己创建好队列和Exchange进行binding时填入

不要以为每天把功能完成了就行了,这种思想是要不得的,互勉~!

若对本页面资源感兴趣,请点击下方或右方图片,注册登录后

搜索本页相关的【资源名】【软件名】【功能词】或有关的关键词,即可找到您想要的资源

如有其他疑问,请咨询右下角【在线客服】,谢谢支持!

死信队列和延时队列 (死信队列和延迟队列的理解) 第2张

发表评论

评论列表

  • 这篇文章还没有收到评论,赶紧来抢沙发吧~
欢迎你第一次访问网站!