phpcms做网站好吗,seo描述快速排名,做软件,如何建立营销型网站前言
在使用消息队列时不可避免的会遇到顺序消费、重复消费、消息丢失三个问题。在一次面试字节的时候#xff0c;面试官问到如何保证顺序消费#xff0c;当时回答不太准确#xff0c;特意此文回顾如何解决顺序消费、重复消费、消息丢失三个问题。
重复消费
解决重复消费…前言
在使用消息队列时不可避免的会遇到顺序消费、重复消费、消息丢失三个问题。在一次面试字节的时候面试官问到如何保证顺序消费当时回答不太准确特意此文回顾如何解决顺序消费、重复消费、消息丢失三个问题。
重复消费
解决重复消费的关键在于消费方的幂等 幂等idempotent、idempotence是一个数学与计算机学概念常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。 幂等函数或幂等方法是指可以使用相同参数重复执行并能获得相同结果的函数。这些函数不会影响系统状态也不用担心重复执行会对系统造成改变。 幂等操作根据场景的不同可以分为
1强校验
场景如与金钱相关的支付等关键消息必须强校验。
基于数据库的唯一键来保证重复数据不会被插入多条。建立一个已消费消息的表每次消费之前检查消费表中当前消费的消息是否已经存在若存在表示消息已经被消费过直接返回。
2弱校验
场景可以有小概率出现重复消费的非关键消息
基于Redis的实现
使用set结构实现。每次消费前查看set中是否已经存在待消费的消息的唯一标识符不存在则消息存在则直接返回。场景唯一标识id作为Redis的key并设置一定的过期时间。每次消费时检查key是否已经存在存在则直接返回
顺序消费
RocketMQ提供两种顺序消息模式
普通顺序消息 普通顺序消费模式下消费者通过同一个消费队列收到的消息是有顺序的不同消息队列收到的消息则可能是无顺序的。普通顺序消息在 Broker 重启情况下不会保证消息顺序性 (短暂时间) 。严格顺序消息 严格顺序消息模式下对于指定的一个 Topic所有消息按照严格的先入先出FIFO的顺序进行发布和消费。严格顺序消息 即使在异常情况下也会保证消息的顺序性 。
严格顺序虽然能更好的保证消息有序但实现它可会付出巨大的代价。如果你使用严格顺序模式Broker 集群中只要有一台机器不可用则整个集群都不可用。 一般而言我们的 MQ 都是能容忍短暂的乱序所以推荐使用普通顺序模式。
顺序消费的实现
在MQ的模型中顺序需要由3个阶段去保障
消息被发送时保持顺序消息被存储时保持和发送的顺序一致消息被消费时保持和存储的顺序一致
消息被发送时保持顺序
使用严格顺序模式
严格顺序消息模式下对于指定的一个 Topic所有消息按照严格的先入先出FIFO的顺序进行发布和消费。因此只要保证消息同步发送发完一条后再发下一条即可保证消息发送时保持顺序。
使用普通顺序模式
普通顺序模式下只有同一个队列的消息能保证有序。Producer 生产消息的时候会进行轮询(根据设定的负载均衡策略)来向同一主题的不同消息队列发送消息。那么如果此时有几个消息分别是同一个订单的创建、支付、发货在轮询的策略下这 三个消息会被发送到不同队列 因为在不同的队列此时就无法使用 RocketMQ 带来的队列有序特性来保证消息有序性了。 因此使用普通顺序时在同步发送的基础上还需要将消息发送到相同的队列。 在RocketMQ中通过MessageQueueSelector来实现队列的选择。通过对订单的唯一标识符取hash将同一个订单的消息发送到相同的队列。
消息被消费时保持和存储的顺序一致
在分布式的情况下即使消息队列有序的将消息发送给消费者也可能因为网络等原因导致消费者接收到的消息无序。如按顺序发送消息a、b给消费者。虽然a先发送但因为网络原因消息a在网络中滞留一段时间导致消费者收到的消息顺序为b、a。同时若同一个队列的消息由不同消费者消费也可能出现以上情况。
消费者顺序消费消息的实现
基于以上分析要保证消息顺序的被消费者消费必须满足下列条件
同一个订单的消息由同一个消费者消费消费者消费完一条消息之后才可以接着消费下一条
Rocket MQ的负载均衡策略规定Consumer数量应该小于等于Queue数量如果Consumer超过Queue数量那么多余的Consumer 将不能消费消息。 在一个Consumer Group内Queue和Consumer之间的对应关系是一对多的关系一个Queue最多只能分配给一个Consumer一个Cosumer可以分配得到多个Queue。这样的分配规则每个Queue只有一个消费者可以避免消费过程中的多线程处理和资源锁定有效提高各Consumer消费的并行度和处理效率。
1同一个消费者消费
类似于通过订单id的hash选择相同的队列可以通过订单的hash选择同一个消费者同步消费消费完一条后再拉取下一条单线程消费保证同一个订单的顺序消费
2通过 consumer 内部用内存队列做排队然后分发给底层不同的 worker 实现实现复杂
若消费者是多线程此时在消费者内部建立内存队列。先将消息拉取到内存队列后在分发给不同的线程
消息丢失
消息的可靠性需要由3个阶段去保障
发送端消息可靠性存储端消息可靠性消费端消息可靠性
发送端消息可靠性
消息发送一般有以下几种方式同步发送、异步发送以及单向发送业务具体选择哪种方式进行消息发送需要根据情况进行判断下面具体介绍不同的发送方式实现的消息可靠性保证。
1同步发送
同步发送是指发送端在发送消息时阻塞线程进行等待直到服务器返回发送的结果。发送端如果需要保证消息的可靠性防止消息发送失败可以采用同步阻塞式的发送然后同步检查Brocker返回的状态来判断消息是否持久化成功。如果发送超时或者失败则会默认重试2次RocketMQ选择至少传输成功一次的消息模型但是因为网络传输是不可靠的有可能发生重复投递。
2异步发送
异步发送是指发送端在发送消息时传入回调接口实现类调用该发送接口后不会阻塞发送方法会立即返回回调任务会在另一个线程中执行消息发送结果会回传给相应的回调函数。具体的业务实现可以根据发送的结果信息来判断是否需要重试来保证消息的可靠性。
3单向发送
单向发送是指发送端发送完成之后调用该发送接口后立刻返回并不返回发送的结果业务方无法根据发送的状态来判断消息是否发送成功单向发送相对前两种发送方式来说是一种不可靠的消息发送方式因此要保证消息发送的可靠性不推荐采用这种方式来发送消息。
存储端消息可靠性
存储端的可靠性依靠持久化策略、备份主从复制保证
RocketMQ刷盘机制
同步刷盘
消息写入内存的 PageCache后立刻通知刷盘线程刷盘然后等待刷盘完成刷盘线程执行完成后唤醒等待的线程返回消息写成功的状态。这种方式可以保证数据绝对安全但是吞吐量不大。
异步刷盘
消息写入到内存的 PageCache中就立刻给客户端返回写操作成功当 PageCache中的消息积累到一定的量时触发一次写操作或者定时等策略将 PageCache中的消息写入到磁盘中。这种方式吞吐量大性能高但是 PageCache中的数据可能丢失不能保证数据绝对的安全。
消费端消息可靠性
1消费重试
消费者从RocketMQ拉取到消息之后需要返回消费成功来表示业务方正常消费完成。因此只有返回CONSUME_SUCCESS才算消费完成如果返回CONSUME_LATER则会按照不同的messageDelayLevel时间进行再次消费时间分级从秒到小时最长时间为2个小时后再次进行消费重试如果消费满16次之后还是未能消费成功则不再重试会将消息发送到死信队列从而保证消息存储的可靠性。
2死信队列
未能成功消费的消息消息队列并不会立刻将消息丢弃而是将消息发送到死信队列其名称是在原队列名称前加%DLQ%如果消息最终进入了死信队列则可以通过RocketMQ提供的相关接口从死信队列获取到相应的消息保证了消息消费的可靠性。
3消息回溯
回溯消费是指Consumer已经消费成功的消息或者之前消费业务逻辑有问题现在需要重新消费。要支持此功能则Broker存储端在向Consumer消费端投递成功消息后消息仍然需要保留。重新消费一般是按照时间维度例如由于Consumer系统故障恢复后需要重新消费1小时前的数据。RocketMQ Broker提供了一种机制可以按照时间维度来回退消费进度这样就可以保证只要发送成功的消息只要消息没有过期消息始终是可以消费到的。
参考
冒着期末挂科的风险也要给你看的消息队列和RocketMQ入门总结(全面、易懂)聊一聊顺序消息RocketMQ顺序消息的实现机制分析透彻好文rocketmq-常见问题总结(消息的顺序、重复、消费模式)分析透彻好文字节跳动面试官这样问消息队列分布式事务、重复消费、顺序消费我整理了一下比较全面Rocket官网面试官再问我如何保证 RocketMQ 不丢失消息这回我笑了RocketMQ如何保证消息的可靠性分布式消息队列如何保证消息的顺序性消息队列之如何保证消息的顺序性