企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
前面有介绍过消息队列的好处,通过理解这些好处,我们就能找到其应用场景。 事情大多都有两面性,而消息队列,当我们享受它的便捷时,也需要考虑到一些注意事项。 > 这里,举一些常见的注意事项,使用过程中,因为项目背景与具体情况,还会延伸出更多需要注意的东西,那些需要我们自己在遇到时进行分析,并寻找解决方案,然后记录下来,充实自己的知识。 # 复杂度 最简单的架构可能只有 PHP+MYSQL ,此时,复杂度是低的。 我们遇到问题,需要考虑的可能性也会较少,需要检查的部件也比较少。 而当我们开始使用消息队列,我们自然而然需要考虑到消息队列的相关信息,复杂度,自然提升了。 当然,这可能无法避免。 > 比如,生活中,我们使用电风扇,可以吹到凉风。但如果我们还需要制热,就会购买空调。电风扇若是坏了,我们或许能自己修回去,但若是空调坏了,我们可能需要有一定专业知识,才能修理。这就是复杂度。 **当能力变强的同时,复杂度也会提升,这一点通常无法避免,除非,我们找到更多的方案,再分析方案间的利弊,最后决策** # 可用性 因为生产者和消费者都依赖于消息队列,比如订单系统使用了消息队列,那么,我们需要考虑到消息队列的可用性,消息队列一旦崩溃,订单系统便不能正常运转,而任何依赖于订单系统的功能,也将无法提供服务。 > 虽然,消息队列出现崩溃的概率相对较低,但在选型初期和设计架构的时候,还是要充分考虑,并尽可能设计好预备方案。 一般而言,常见的消息队列系统,会通过分布式来提高可用性。 # 消息可靠性 在消息队列使用过程中,我们可能会遇到这种情况: - 生产者将消息发出,但消息队列没有接收到 这很可能是网络原因,比如,生产者发出消息,但由于网络或者消息队列方面的一些原因,最终,消息没有被存入指定的队列中,此时,可能引起消息丢失。 > 一般我们会要求消息队列返回一个确认信息,告诉生产者,消息放好了。如果,生产者迟迟未能接收到确认信息,则再次发送消息。以此来保证消息不被丢失。 - 消息队列崩溃或服务器宕机,内存清空,数据丢失 当遇到这种情况,会导致其存储在内存中的数据丢失,消息的可靠性就得不到保证。 > 一般我们会通过持久化来保证消息不丢失,不同的消息队列系统,持久化的方式未必一样,具体的使用,将会在后面章节详细介绍。 - 消费者崩溃,无法正确处理消息 还有一种情况,当消费者从消息队列中读取出消息,消息状态变成了 reserved 或者,干脆直接在队列中被删除,而此时,消费者却在执行后面的程序时崩溃了,导致业务流程没有顺利执行,但消息已经被取出。 **这也是一种数据丢失的现象。** > 一般此时,也需要消费者与消息队列之间建立反馈机制,一定要当在消费者处理完消息之后,给消息队列发送明确的删除指令,才能删除消息。 另外消息队列这边,可以建立 TTR( time-to-run , 超时重发)机制,如果队列中处于 reserved 状态的消息过了一定时间还未被删除,可以将之丢回 ready 状态,继续被消费者读取 # 顺序消费 **首先,顺序消费和顺序保证,是两回事** 顺序保证是指同类型的消息被消费的先后顺序,而顺序消费,是指,当一个消息,需要被多个消费者按一定顺序先后消费。 例如,当消息队列中存放一个订单信息时,我们有以下消费者: - 发短信 - 发邮件 - 发 APP 推送 而我们必须先发短信、再发邮件、再发 APP 推送,不可提前,不可押后。 这就是一种顺序消费的场景。 一般来讲,有两种比较常见的解决方案: - 合并消费者 这种方式一般不推荐,但前期实现起来复杂度较低。 它意味着将多个消费者合并为一个消费者,再顺序执行。 虽然可以实现逻辑,但本身却降低了可扩展性,提高了耦合,这对未来维护项目而言,会增加隐性成本。 - 控制消费状态 还有一种方案,我们在生产者中放入短信消息,当短信消息被处理完后,在消费者中往邮件队列放入消息,以此类推。 > 但这也会引起问题,比如,当我们将短信--->邮件--->推送串联好,但我们又要在短信和邮件之间,插入一个新的消费者,此时,我们除了要新增消费者以外,还需要修改短信消费者。 **这种设计就像是一个链表,头是生产者,指向短信,短信指向邮件,此时中间插入一个,需要将短信原本指向邮件的指针,指向新的消费者,而新的消费者则指向邮件。** # 消息重复 前面有说过,为了保证消息可靠性,我们可能会让生产者重复发送消息(当迟迟没有收到消息队列反馈的时候)。 但是,这种情况,大多数时候是由网络不稳定引起的。 也就是说,虽然消息队列的反馈,可能迟到,但并不代表,消息已经丢失了。 此时,生产者再次往消息队列发送消息,则消息队列可能会收到两条重复的消息。 > 这里举个例子:比如消费者需要为用户余额加 5 元钱,如果消息重复,就有可能为用户加上好几个 5 元。 我们一般通过保证消费者被重复执行的幂等性来避免消息重复带来的问题。 **幂等性指:多次进行同样的操作,执行结果都一致** 比如,你输入的数据在不变的情况下,你的算法应保证处理结果是一致的,特别是在业务逻辑上。 为什么要通过保证消费者的幂等性来避免消息重复? 因为我们无法保证,网络不出现拥堵,不出现故障。 所以,我们无法保证,生产者不重复往队列中写入消息。 因此,比较好的方案,是在消费者上面进行控制。 **比如,幂等性能够保证,同样一条消息,尽管被消费者读取多次,但在现实中产生的影响,却只有一次。** 至于如此保证幂等性,会是一个比较复杂的问题,我们需要根据项目的实际情况和实际架构,进行具体的设计。 # 一致性 因为消息队列赋予了异步能力,所以,当我们生产者在调用的时候,并非业务逻辑已经成功执行完。 比如,我们在生产者中调用发短信,实际上,短信并未发出去,只是将要发短信的任务存储到队列中,等到被执行。 那么,此时给调用返回的结果,只是调用的结果,并不等于发送成功。 **所以,我们无法以调用结果来判定业务处理结果。** 这个就是暂时不一致性。 这个结果,暂时的,与你所知的不一致。 但,它最终会一致,也就是说,最终,消息队列内的每一条消息都会被取出来消费。 这个就是最终一致性。