本指南将帮助您了解 Pub/Sub 的可靠性功能,并为您提供这些功能的整体概览。
为何选择 Pub/Sub?
作为一种消息传递范式,发布/订阅旨在将消息的提供方与这些消息的使用方分离开来。提供方不会直接向使用方发送包含数据的请求,而是将这些数据发布到 Pub/Sub 等 Pub/Sub 服务。该服务会将这些消息异步传递给已订阅的感兴趣的使用方。
这样一来,该服务便可处理寻找对数据感兴趣的使用者的所有复杂问题。该服务还会根据使用方的容量管理其接收数据的速率。这种解耦使数据生成者能够以低延迟大规模写入消息,而不受使用者的行为影响。
Pub/Sub 提供高度可伸缩、可靠的消息传递服务。虽然该服务会自动处理大部分此类问题,但您可以控制发布者和订阅者的不同方面,这些方面可能会影响可用性和性能。本指南的其余部分将详细介绍这些方面。
隔离
默认情况下,Pub/Sub 是一项全球服务:主题和订阅本质上不与特定区域绑定,消息会根据需要在区域之间的 Pub/Sub 服务中流动。使用全球端点 pubsub.googleapis.com 时,发布者和订阅者会连接到运行 Pub/Sub 的网络最近区域。使用区域端点(例如 pubsub.us-central1.rep.googleapis.com)或位置端点(例如 us-central1-pubsub.googleapis.com)时,发布方和订阅方会连接到指定区域中的 Pub/Sub。在 Google Cloud之外运行发布者或订阅者时,最好使用区域或位置端点,以确保消息在预期区域之间一致地流动。
区域隔离
如需最大限度地减少发布和订阅操作在单个区域之外所依赖的基础设施,并确保所有数据都与该区域隔离,请按照以下步骤操作:
为每个区域创建一个主题。
虽然 Pub/Sub 的命名空间是全球性的,并且您无法将主题和订阅绑定到特定区域,但所有资源的元数据都会复制到该区域内的本地数据存储区。因此,创建资源后,即使其他区域出现问题,其配置也可用。请注意,如果发生服务中断,对主题或订阅配置的更新可能不会立即传播。
避免使用全球端点。
使用消息存储政策,并将
enforceInTransit设置为True。使用强制传输时,数据绝不会离开该区域,并且连接到特定区域中的主题的所有客户端都会将消息存储政策设置为该区域。
通过以这种方式配置主题,您可以确保所有发布和订阅操作都仅在该区域内写入和读取数据。如果单个区域中发生发布者、订阅者或 Pub/Sub 故障,则该区域中的消息传递会停止。其他区域的主题和订阅的消息传递不受影响。
如果您还需要对主题和订阅的管理操作和命名空间进行区域隔离,请考虑使用 Managed Service for Apache Kafka。
故障切换
如果您不需要区域隔离,则可以利用 Pub/Sub 在多个区域之间高效传递消息的能力,以实现多区域故障切换功能。本部分的其余内容将介绍如何创建主题和订阅,以及如何放置发布者和订阅者,以支持不同类型的故障切换和数据冗余。
默认故障切换语义
假设有一个主题和一个订阅。发布者位于美国和澳大利亚的区域,订阅者位于欧洲和澳大利亚的 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 会将消息同步写入一个区域内的多个可用区。因此,可用区级服务中断不足以阻止消息传递;整个区域必须不可用。如果 Pub/Sub 在发布者发送消息的区域中不可用,则在该区域中的消息可能无法传递,直到服务完全恢复:
消息最终仍会传送(假设消息保留期限尚未过去),但会延迟中断的时长。请注意,与订阅者类似,当服务发生故障时,美国境内的发布者也不会故障切换到其他区域。此行为有助于防止因发布者或订阅者故障而导致跨区域级联故障的可能性。
客户控制的故障切换和冗余
如果发布者和订阅者之间发生任何服务中断,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 会提供每个积压指标的两个版本。请务必使用带有 by_region 后缀的版本:
如果您希望监控、提醒和自动扩缩功能能够应对单区域服务中断,请勿使用这些指标的全球版本(subscription/num_undelivered_messages 和 subscription/oldest_unacked_message_age)。这些指标的全局版本需要计算已知有消息的所有区域的积压量,这意味着单个区域不可用会导致数据缺口。相比之下,by_region 版本的指标会按区域计算和报告积压工作。如果无法计算单个区域的积压,该指标仍会报告其他区域的值。
使用快照和跳转功能实现安全部署
消息丢失通常是灾难性事件。Pub/Sub 为发布的所有消息提供至少一次传送。不过,这些消息的正确处理取决于订阅者行为。如果消息已成功确认,Pub/Sub 不会重新传送这些消息。因此,如果您部署的新订阅者代码中引入了 bug,导致在未正确处理消息的情况下确认消息,则可能会导致订阅者引起的消息丢失。Pub/Sub 提供快照和还原功能,可帮助您确保正确处理每条消息,即使订阅者存在 bug 也是如此。
每个订阅者部署的模式必须如下所示:
在确定新订阅者是否正常运行之前,您需要等待的时间可能因您的用例而异。退出步骤流程的唯一方法是,当订阅者被视为正常工作时,此时可以删除快照。
使用快照和跳转功能并不能取代以下最佳实践:首先在非生产环境中运行软件,然后逐步部署到生产环境。它们提供额外的保护级别,以确保可靠地处理数据。但代价是,还原到快照可能会导致订阅者已成功处理的消息被重复传送。不过,由于 Pub/Sub 默认采用至少一次传送语义,因此您的订阅方已经能够应对消息重新传送。