本指南可帮助您全面了解 Pub/Sub 可靠性特性。 本文档涵盖的主题包括:
- 为何要使用 Pub/Sub?
- 故障切换
- 微调发布商
- 微调订阅者
- 使用快照和还原功能进行安全部署
为何要使用 Pub/Sub?
作为消息传递范式,发布-订阅旨在将消息的提供方与消息的使用方分离开来。生产者不再向使用方发送直接请求随数据,而是将数据发布到 Pub/Sub 服务(例如 Pub/Sub)。服务会将这些消息异步传送给已订阅且感兴趣的使用方。
其结果是,该服务解决了查找对数据感兴趣的消费者的所有复杂工作。该服务还根据使用方的容量管理接收数据的速率。通过分离,数据提供方能够以低延迟的方式大规模写入消息,而无需考虑使用方的行为。
Pub/Sub 提供伸缩能力极强且可靠的消息传送服务。虽然该服务会自动处理大部分工作,但您可以控制发布者和订阅者的不同方面,这些方面可能会影响可用性和性能。本指南的其余部分将详细介绍这些方面
故障切换
Pub/Sub 是一项全球性服务:主题和订阅本身并不与特定区域相关联,并且消息会根据需要在 Pub/Sub 服务之间流动。使用全球端点 pubsub.googleapis.com
时,发布者和订阅者连接到运行 Pub/Sub 的网络最近的区域。使用位置端点(例如 us-central1-pubsub.googleapis.com
)时,发布者和订阅者将连接到指定区域中的 Pub/Sub。在 Google Cloud 之外运行发布者或订阅者时,最好使用位置端点,以确保消息在预期区域之间一致地流动。本部分的其余内容介绍如何创建主题和订阅。此外,我们还讨论了如何放置发布者和订阅者来支持不同类型的故障切换和数据冗余。
默认故障切换语义
假设只有一个主题和订阅。发布者位于美国和澳大利亚的区域,而订阅者位于欧洲和澳大利亚的 Google Cloud 区域。如果所有订阅者都有足够的容量来接收消息,则消息流如下所示:
其中 P 代表发布商,S 代表订阅者。蓝色六边形表示 Pub/Sub 服务。圆柱面表示存储消息的位置(消息始终持久保存在消息发布区域的多个可用区中)。Pub/Sub 倾向于在有订阅者可用时,在发布消息的同一区域内发送消息。否则,它会将消息发送到具有有容量订阅者的网络最近的区域。因此,如上图所示,在美国发布的消息将传送给欧洲的订阅者,而在澳大利亚发布的消息则留在澳大利亚。
以下部分讨论了不同故障场景中会发生的情况。
无法选择欧洲境内的订阅者
假设欧洲的订阅者服务故障或频繁崩溃,并且无法保持与 Pub/Sub 的连接。如果发生这种情况,服务将开始向澳大利亚境内的订阅者传送消息:
无法查看欧洲和澳大利亚的订阅者
如果所有订阅者都不可用,Pub/Sub 会存储消息,直至达到配置的消息保留时长。
订阅者重新连接后,除非中断持续时间超过配置的消息保留期限,否则消息将被传送。默认情况下,订阅消息保留期限设置为 7 天。您还可以配置主题的消息保留期限(最长可达 31 天)。选择的消息保留期限不要短于您预期或愿意容忍的最大中断。
Pub/Sub 在欧洲无法使用
虽然这种情况很少见,但您可能还需要处理 Pub/Sub 本身不可用的情况。Pub/Sub 清单本身不可用,表现为长时间在发布或订阅请求中出现意外错误,或者无法将已发布的消息传送给订阅者。例如,如果 Pub/Sub 在欧洲的区域中关闭,则场景与订阅者服务中断时的情况大致相同:
请注意,在这种情况下,即使使用全球端点,欧洲的订阅者也不会故障切换到其他区域。Pub/Sub 有意不会自动进行故障切换。假设是订阅者本身在 Pub/Sub 中引发意外问题,进而导致不可用。此类问题被视为严重的服务中断。但是,服务中断的影响范围可限定为订阅者连接到的区域。如果服务允许它们故障切换到其他区域,则订阅者也可能会导致该区域不可用,从而导致服务级联故障。
无法定位到澳大利亚境内的发布商
如果某个区域中的发布者不再可用,则已经发布的消息仍会传送给距离最近的订阅者:
最终,所有消息都被订阅者使用和确认。发送消息时,Pub/Sub 会尝试尽可能缩小网络距离。因此,如果欧洲地区的订阅者有足够的容量来处理在美国发布的所有消息,则澳大利亚区域的订阅者可以停止接收消息。
Pub/Sub 无法在美国使用
Pub/Sub 会将消息同步写入一个区域内的多个可用区。因此,可用区级服务中断不足以阻止消息的传送;整个区域必须不可用。如果 Cloud Pub/Sub 在发布者发送消息的区域不可用,则在服务完全恢复之前,该区域中的消息可能无法传送:
消息最终仍会传送(假设消息保留期限尚未过),它会延迟一段时间的中断时间。请注意,与订阅者类似,服务发生故障时,美国的发布商也不会将故障切换到其他区域。此行为有助于防止因发布者或订阅者故障而导致跨区域发生级联故障的可能性。
隔离
默认故障切换语义详细影响了数据隔离,以及发布者、订阅者或 Pub/Sub 本身不可用对消息流的影响。您的用例可能需要不同级别的隔离。例如,您可以要求所有消息在区域内传送。
如果不需要隔离,那么详细的默认故障切换语义就足够了。您必须创建单个主题和单个订阅,并将发布者和订阅者放置在所有所选区域中。如果订阅者不可用或 Pub/Sub 在其连接的区域中关闭,则传送服务会将消息故障传递给其他区域中的订阅者。
对于区域隔离(保证数据不会离开某个区域),请创建主题和订阅来处理每个区域中的消息。在每个区域中找到发布者和订阅者,并让他们分别发布和订阅相应的区域主题和订阅。您还必须使用区域端点来确保数据仅在区域内移动。如果单个区域中发生发布者、订阅者或 Pub/Sub 失败,消息将在该区域中停止传送。其他区域的主题和订阅的消息传送不受影响。
最后,可用区隔离(即数据保证位于单个可用区内)在 Pub/Sub 中不可能实现。如果您要求各个地区是独立的,请使用 Pub/Sub Lite。
由客户控制的故障切换和冗余
Pub/Sub 的默认故障切换语义可能无法完全保证消息始终可以从发布者流向订阅者(如果介于两者之间的任何地方出现中断)。服务中断可能会发生在多个不同的位置,包括客户端、发布者或订阅者运行的服务中、网络中,甚至很少出现在 Pub/Sub 本身中。如果您需要让服务能够应对此类服务中断,则必须实现自己的冗余。通常,这些冗余包括使用发布者和订阅者客户端的多个实例,其中每个实例使用不同的位置端点。
您可能希望能够弹性受两个不同影响范围(可用区级或区域级)的影响。以下是各种工具的设置选项。
可用区级弹性
Pub/Sub 具有内置的跨可用区复制功能。您无需采取任何特殊步骤来处理影响服务本身的单可用区服务中断。但是,为了让客户端或网络能够灵活应对中断,最好在区域内的多个可用区中运行有足够容量的发布者和订阅者。如果一个可用区发生故障,则另一个可用区中的客户端能够接收流量并处理消息。最佳做法是不要同时发布对这些客户端的更改,以便在引入 bug 时,其他不受影响的区域可以继续处理消息。
区域弹性
为了应对区域性故障,请在发布者和订阅者中设置额外的冗余。您可以在多个区域运行发布方和订阅者,以应对这些客户端或网络可能出现的中断。
如果您希望灵活应对某个区域中潜在的 Pub/Sub 故障,则必须准备好故障切换机制来处理此类中断。可能的方法是在端到端消息传送延迟时间与您的费用之间取得平衡的方法。
要在不考虑费用的情况下最大限度地减少延迟,最佳策略是始终在不同区域中同时发布和订阅。首先,选择需要冗余的区域的数量。接下来,虽然不是绝对必要,但您可以为其中每个区域设置主题和订阅。
每个发布者创建的发布者客户端数量与区域(每个区域一个)相同,并使用不同的位置端点来确保消息定向到不同的区域。如果使用单独的主题,则每个发布者客户端必须发布到每个区域的相应主题。对于每条消息,发布者调用都会在每个客户端上发布。有了冗余发布,只要发布失败,就无需重新尝试发布。
同样,每个订阅者都会创建这么多订阅者客户端(每个区域一个),并使用位置端点连接到不同区域。如果对每个区域使用不同的订阅,则每个订阅者客户端必须使用相应的订阅。请注意,用于发布者和订阅者的区域不必相同。订阅者通过这三个订阅接收消息并处理这些消息。
此设置具有一些关键功能和要求:
- 任何单区域服务中断都不会影响对已发布消息的处理,也不会影响服务中断期间发布的消息的处理。由于消息已发布到多个区域,因此即使一个区域出现故障,这些消息仍然可在其他区域使用。在服务中断期间,发布调用在受影响的区域中失败,但在其他区域中成功。
- 只要消息流经的任何区域可用,消息处理延迟时间就不会受到影响。
- 消息处理必须具有幂等性。由于每条消息都将多次传送,因此消息处理必须能够灵活应对重复消息。如果发生地区性服务中断,其中一些重复项可能比首次传送消息的时间晚得多。这些重复项可能来自未遇到服务中断的其他区域。
以这种冗余方式运行可为任何类型的服务中断提供最高的弹性。对于依赖于 Pub/Sub 且需要最高可用性的内部 Google 服务,最好使用此设置。不过,此设置需要将消息传递费用乘以使用的区域数。对于必须跨区域移动的消息,还会产生额外的区域间网络使用费。
另一种冗余方法是仅在请求失败或消息未按预期从发布者流向订阅者时进行故障切换。在此场景中,您有一个主要区域,您通过位置端点将发布者和订阅者定向到该区域。与之前一样,这些区域不必是同一个区域。然后,您还可以为发布者和订阅者提供后备区域,以便在主要区域不可用时使用。
当发布商的请求成功发送后,它仅会发布到主要区域(通过位置端点)。每当确定该区域发生故障时,发布商就会开始将内容发布到回退区域。您可以通过两种方式确定该区域是否已关闭并执行故障切换。此操作可通过手动流程完成,并且配置会在发布商中动态更新。如果发布请求中的错误率足够高,发布商也可以自行更新配置。
订阅者必须始终通过位置端点连接到主要区域。您可以决定订阅者可以将回退区域与以下一个或多个触发器搭配使用:
- 始终订阅后备区域。在这种情况下,订阅者始终与主要区域和后备区域保持连接。发布者和订阅者可以使用相同的区域作为主区域和后备区域。在这种情况下,只有在发布方发生故障切换时,订阅者才能通过备份区域接收消息。
- 通过配置手动检测订阅者并将其切换到回退区域。如果检测到服务中断,您可以故障切换到后备区域,然后在服务中断消退后迁移回主要区域。
- 发生订阅者错误时进行故障切换。如果订阅者请求返回错误,您可以使用此指示,您必须故障切换到回退区域。请注意,出现暂时性错误时,Pub/Sub 客户端库会在内部重试流式拉取请求,因此您可能无法检测到长时间的意外错误。此外,即使在正常操作期间,流式拉取错误率预计为 100%。
- 如果订阅者经历了意外超长的时间而未收到消息,则进行故障切换。假设消息的发布一致,则订阅者可以始终接收消息。如果这些访客在很长一段时间内未收到任何消息,则主要区域的 Pub/Sub 中可能存在订阅端问题。通过切换到后备区域可以解决此问题。
在全部四个选项中,第一个是理想的。如果订阅者连接没有传输消息,则不会产生任何费用。唯一的费用在于订阅者客户端库的其他实例所占用的空间,此占用空间可以忽略不计。您还必须注意每个区域打开的流式拉取连接的数量配额。
第二种模型的优点在于,由于消息只发布一次,因此 Pub/Sub 费用没有成倍增加。但弊端是,对于某些类型的服务中断,在服务中断开始前发布的消息可能只有在服务中断解决后才可用。存储在该区域不可用的消息可能无法传送给订阅者,无论他们连接到何处都是如此。在服务中断期间发布到回退区域的消息可以获取。此外,发布商或订阅者的错误率会增加,进而导致服务不可用。这取决于用于检测服务中断的方法以及故障切换到回退区域的时间。
无论您选择哪个选项,都请注意这可能会与 Pub/Sub 的功能交互。订购配送和正好一次配送均在区域内提供保证。例如,如果使用故障切换冗余技术,则只有在同一区域中发布的消息才能保证传送消息。订阅者可以先收到发布到回退区域的消息,然后再将其发布到主要区域,即使消息先发布到主要区域也是如此。
微调发布商
无论您选择哪些故障切换选项,都需要在发布商内部执行一些额外的调整步骤。通过调整发布商的行为,可确保在高负载下实现最佳性能。批处理消息是一种权衡延迟以降低费用的方法,但不太注重可靠性,因此本文未进行介绍。相反,您可以专注于对可靠性进行微调的一些其他参数,包括重试设置和流控制设置。
发布失败的原因可能有多种,包括网络不可用等暂时性原因,或权限更改等需要用户干预的原因。Pub/Sub 客户端库使用重试设置中指定的参数重试暂时性错误。这些设置用于控制因暂时性原因而失败的发布 RPC 重试时指数退避算法的行为。虽然默认设置通常可以适用于大多数情况,但在某些情况下,您可能需要调整这些值。
您最有可能需要调整的两个属性是初始 RPC 超时和总超时。初始 RPC 超时是让第一个发布 RPC 完成的时长。如果任何 RPC 失败或超时,系统会尝试运行另一个 RPC 并延长超时时间,直到超过请求总数或超过总超时时间。
如果您的发布者网络受限或距离运行 Pub/Sub 的最近 Google Cloud 数据中心很远,则可以调整初始超时时间。网络限制可能是对运行发布者所在机器的吞吐量的限制,也可能是由于在同一台网络密集型机器上运行的其他服务造成的。如果超时设置过短,初始 RPC 可能会反复失败,导致成功发布需要更多尝试(超时时间较长)。反复重试会增加发布延迟时间。在这种情况下,增加初始超时时间可以加快发布速度。
如果网络连接不可靠,则增加总超时和初始超时会有所帮助。总超时增加可让发布 RPC 有更多时间成功完成。如果发布 RPC 持续失败并给出超出截止日期的错误,请考虑调整这些值。
如果发布时连续超出时限错误,则可能表示需要调整发布方流控制。通过这些设置,您可以确保发布者能够灵活应对传入流量激增(这些激增会导致更多消息发送到 Pub/Sub)。传出请求的大量增加可能会使发布商的 CPU、内存或网络容量过载。如果发布过载,便无法在超时之前处理发布请求或响应。这会导致发布更多请求,并最终达到总超时时间。发布者流控制可限制无发布请求响应时可未完成的消息或字节数。以这种方式限制请求数,可将资源利用率保持在易管理的水平,即使在高峰期也是如此。根据发布者的运营方式,您可以允许发布以阻止更多请求,从而允许后续发布 RPC 等待处理能力。或者,您也可以通过让流控制在达到容量上限时返回错误,将请求推送回服务的调用方。您可以配置发布商客户端库如何响应超出限制的行为。
微调订阅者
为确保可靠运行,可能还有必要进行订阅者调优。与发布者类似,您可以调整订阅者的流控制设置,以确保订阅者不会不堪重负。订阅者客户端库使用流式拉取,在这种模式下,客户端打开向服务器的永久性流,服务器会在消息可用时发送消息。如果已发布的消息大量增加,订阅者收到的消息可能会超过其处理能力。实施流控制后,一次向客户端执行的未确认消息的数量会受到限制。这样可以减少同时处理的消息数量,并将处理时间分散到较长的一段时间。分散负载可让订阅者始终受到任何影响消息处理的资源限制,这可能会导致级联效应,进而导致无法处理任何消息。
如果您仅预计要处理的数据量激增并最终消退,仅采用流控制就足够了。如果流量因使用量增加而逐渐增加,则流控制可以保护订阅者。但是,这可能会导致积压消息不断累积,并导致消息在消息保留时长过后无法传送。在这种情况下,您可能还需要设置自动扩缩,以便增加更多订阅者,以应对越来越多的未确认消息。如何设置取决于您为订阅者使用的计算平台。例如,Compute Engine 的自动扩缩器可让您根据未传送消息数量等指标进行扩缩。通过同时使用自动扩缩和流控制,您可以确保订阅者能够灵活应对消息吞吐量的其他短期峰值和需要更多计算能力的长期增长。
使用快照并寻求安全部署
消息丢失通常是灾难性事件。Pub/Sub 会为发布的所有消息提供至少一次传送。但是,是否正确处理这些消息取决于订阅者行为。如果消息成功确认,Pub/Sub 不会重新提交。因此,如果您部署的新订阅者代码中引入了一个在未正确处理消息的情况下确认消息的 bug,则可能会导致订阅者引起的消息丢失。Pub/Sub 提供快照和跳转功能,可帮助您确保正确处理每条消息,即使遇到订阅者错误也是如此。
每个订阅者部署的模式都必须如下所示:
在确定新订阅者是否正常工作之前,等待的时间可能会因您的用例而异。退出步骤流程的唯一方法是当订阅者被视为工作正常时,此时可以删除快照。
使用快照和跳转并不意味着取代关于首次在非生产环境中运行软件以及逐步部署到生产环境的最佳实践。它们提供了额外的保护级别,以确保数据的可靠处理。但需要权衡的是,还原至快照可能会导致重复传递订阅者已成功处理的消息。但是,考虑到 Pub/Sub 默认具有“至少传送一次”语义,您的订阅者已经能够灵活应对消息重新传送。