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

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

热门资讯

死信队列 (死信队列和延时队列的区别)

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

死信队列和延时队列是消息队列中常见的两个概念,它们在处理消息投递、重试和失败处理方面起到关键作用。本文将详细分析死信队列和延时队列的区别,以帮助读者更全面地理解它们在消息队列系统中的作用。

1. 死信队列

死信队列是一种用来处理无法被消费者正确处理的消息的队列。当消息在被消费者处理时出现异常、超时或者被拒绝时,这些消息会被发送到死信队列中。通过死信队列,系统可以对这些异常情况进行后续处理,如记录日志、重试、丢弃等。

死信队列的优势在于能够有效处理消费者无法处理的消息,避免消息丢失或被忽略。通过死信队列,系统可以对异常情况进行监控和处理,确保消息的最终处理结果。

2. 延时队列

延时队列是一种用来延迟消息投递的队列。消息被发送到延时队列后,会在指定的时间间隔后才被投递给消费者。这种机制常用于处理需要延迟执行的任务,如定时任务、延时通知等。

延时队列的特点在于可以根据业务需求设定消息的延迟时间,灵活控制消息的投递时机。通过延时队列,系统可以实现定时任务、延时通知等功能,提高系统的灵活性和可控性。

3. 区别对比

死信队列和延时队列在功能和使用场景上有明显的区别:

功能:

- 死信队列用于处理消费者无法正确处理的消息,确保消息的最终处理结果。

- 延时队列用于延迟消息投递,实现定时任务、延时通知等功能。

使用场景:

- 死信队列适用于需要处理异常消息的场景,如重试、记录日志、丢弃等。

- 延时队列适用于需要延迟执行任务的场景,如定时任务、延时通知等。

总结:

死信队列和延时队列在消息队列系统中扮演着不同的角色,分别用于处理异常消息和延迟消息。通过合理地使用死信队列和延时队列,系统可以更好地管理消息的投递和处理,提高系统的稳定性和可靠性。


RocketMQ使用之消息保证,重复读,积压,顺序,过滤,延时,事务,死信

消息可能在哪些阶段丢失呢?可能会在这三个阶段发生丢失: 生产阶段 、 存储阶段 、 消费阶段

所以要从这三个阶段考虑:

在生产阶段,主要通过请求确认机制,来保证消息的可靠传递。

存储阶段,可以通过配置可靠性优先的Broker 参数来避免因为宕机丢消息,简单说就是可靠性优先的场景都应该使用同步

从 Consumer 角度分析,如何保证消息被成功消费?

Consumer 保证消息成功消费的关键在于确认的时机,不要在收到消息后就立即发送消费确认,而是应该在执行完所有消费业务逻辑之后,再发送消费确认。因为消息队列维护了消费的位置,逻辑执行失败了,没有确认,再去队列拉取消息,就还是之前的一条。

对分布式消息队列来说,同时做到确保一定投递和不重复投递是很难的,就是所谓的 有且仅有一次。 RocketMQ 择了确保一定投递,保证消息不丢失,但有可能造成消息重复。

处理消息重复问题,主要有业务端自己保证,主要的方式有两种: 业务幂等 和 消息去重

发生了消息积压,这时候就得想办法赶紧把积压的消息消费完,就得考虑提高消费能力,一般有两种办法:

顺序消息是指消息的 消费顺序 和 产生顺序 相同,在有些业务逻辑下,必须保证顺序,比如订单的生成、付款、发货,这个消息必须按顺序处理才行。

顺序消息分为 全局顺序消息 和 部分顺序消息:

部分顺序消息相对比较好实现,生产端需要做到把同ID的消息发送到同一个Message Queue;在消费过程中,要做到从同一个 Message Queue 读取的消息顺序处理——消费端不能并发处理顺序消息,这样才能达到部分有序。

发送端使用MessageQueueSelector类来控制 把消息发往哪个Message Queue

消费端通过使用MessageListenerOrderly来解决单Message Queue的消息被并发处理的问题

RocketMQ默认情况下不保证顺序,比如创建一个Topic,默认八个写队列,八个读队列,这时候一条消息可能被写入任意一个队列里;在数据的读取过程中,可能有多个Consumer,每个Consumer也可能启动多个线程并行处理,所以消息被哪个Consumer 消费,被消费的顺序和写人的顺序是否一致是不确定的。

要保证全局顺序消息, 需要先把Topic的读写队列数设置为 一,然后 Producer Consumer的并发设置,也要是一。简单来说,为了保证整个Topic 全局消息有序,只能消除所有的并发处理,各部分都设置成单线程处理 ,这时候就完全牺牲 RocketMQ 的高并发、高吞吐的特性了。

有两种方案:

对消息的过滤有三种方式:

电商的订单超时自动取消,就是一个典型的利用延时消息的例子,用户提交了一个订单,就可以发送一个延时消息, 1h 后去检查这个订单的状态,如果还是未付款就取消订单释放库存。

RocketMQ 是支持延时消息的,只需要在生产消息的时候设置消息的延时级别:

但是目前 RocketMQ 支持的延时级别是有限的:

那么 RocketMQ 怎么实现延时消息的 简单,八个字: 临时存储 + 定时任务 Broker 收到延时消息了,会先发送到主题( SCHEDULE_TOPIC_XXXX )的相应时间段的 Message Queue 中,然后通过一个定时任务轮询这些队列,到期后,把消息投递到目标 Topic 的队列中,然后消费者就可以正常消费这些消息。

半消息 :是指暂时还不能被 Consumer消费的消息, Producer 成功发送到Broker端的消息,但是此消息被标记为暂不可投递状态,只有等Producer端执行完本地事务后经过二次确认了之后, Consumer 才能消费此条消息。

死信队列 (死信队列和延时队列的区别) 第1张

依赖半消息,可以实现分布式消息事务,其中的关键在于二次确认以及消息回查:

RocketMQ 实现消息事务 :

死信队列 用于处理无法被正常消费的消息,即 死信消息

当一条消息初次消费失败,消息队列RocketMQ会自动进行消息重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列RocketMQ不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中,该特殊队列称为 死信队列

死信消息的特点:

死信队列的特点:

RocketMQ控制台提供对死信消息的查询、导出和重发的功能。

NameServer 因为是无状态,且不相互通信的,所以只要集群部署就可以保证高可用。

RocketMQ 的高可用主要是在体现在 Broker 的读和写的高可用, Broker 的高可用是通过集群和主从实现的。

也就是说 Producer 只能向 Master 角色的 Broker 写入消息, Cosumer 可以从 Master 和 Slave 角色的 Broker 读取消息。

Consumer 的配置文件中,并不需要设置是从Master读还是从Slave 读,当Master 不可用或者繁忙的时候,Consumer的读请求会被自动切换到从Slave 。有了自动切换Consumer这种机制,当一个Master角色的机器出现故障后, Consumer仍然可以从Slave读取消息,不影响Consumer读取消息,这就实现了读的高可用。

如何达到发送端写的高可用性

注意 : RocketMQ 目前还不支持 Broker 把 Slave 自动转成 Master ,如果机器资源不足,需要把Slave转成Master,则要手动停止Slave色的Broker,更改配置文件,用新的配置文件启动Broker

延时队列常用实现详解

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

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,跨度足够,放在第三个时间格等待执行,如此往复几次,高层时间轮最终会慢慢移动到低层时间轮上,最终任务到期执行。

WebSphere MQ中的队列有几种啊

可以简单的把队列看作一个容器,用来存放消息。 队列按其定义来分,可以分成本地队列、远程队列、别名队列和模型队列。 其中本地队列是真正意义上的队列实体,可以用来存放消息。 而远程队列和别名队列只是一种队列的定义,指向另一个队列实体。 其中远程队列指向的是其他队列管理器中的队列,而别名队列指向的是本地队列管理器中的队列。 模型队列有一点特殊,它虽然本身只是一个队列的定义,描述了模型的属性,但是当打开模型队列的时候,队列管理其就会以这个定义为模型,创建一个本地队列,被成为动态队列。 一个队列管理器可以管辖多个队列。 但对于一个队列只能隶属于其中的一个队列管理器。 所以队列在其所隶属于的队列管理器只能有唯一的名字,不能于同一个管理器中的其他的队列重名。 当消息添加到队列中是,其缺省被添加到最后。 删除消息时从头开始。 (1)本地队列:本地队列按照功能又可以分为初始化队列、传输队列、目标队列和死信队列。 其中初始化队列用作消息触发。 传输队列只是暂存待传的消息,在条件许可的情况下,通过管道将消息传送其他的队列管理器。 目标队列是消息的目的地,可以长期存放信息。 如果消息不能送达目标队列,也不能路由出去,则被自动放入死信队列。 命令队列指的是WebSphere MQ队列管理器预定义的,任何的MQSC命令都可以发往改队列,并被队列管理器的命令服务器(Command Server)接受处理。 MQXQH(传输信息头,其中的RemoteQName和RemoteQMgrName两个域指名了目标队列和目标队列管理器)MQDLH(死信信息头,其中DestQName和DestQMgrName两个域指名了原消息的目标队列和目标队列管理器,Reason域指名了消息无法投递的原因)。 MQMD(应答消息头,其中ReplyToQ和ReplyToMgr分别表示应答队列和应答队列管理器)(2)别名队列:别名队列的TARGQ属性指名了其代表的目标队列名称,目标队列通常是本地队列。 可以将别名队列理解为指针。 (3) 远程队列:远程队列也是一个定义,使用了远程队列,你就可以不用知道目标队列所在的队列管理器了。 (4) 模型队列:模型队列定义了一套本地队列的属性集合。 一旦打开模型队列,队列管理器就会按照这些属性动态创建处一个本地队列。 模型队列的DEFTYPE属性可以取值为PERMDYN和TEMPDYN分别代表永久动态度列和临时动态队列。

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

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

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

死信队列 (死信队列和延时队列的区别) 第2张

发表评论

评论列表

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