本指南介绍 Pub/Sub 可靠性功能,并提供相关功能的总体概览。
为什么选择 Pub/Sub?
作为一种消息传递范式,发布-订阅旨在将消息的生产者与消息的使用者分离。生产者不再直接向消费者发送包含数据的请求,而是将数据发布到 Pub/Sub 等 Pub/Sub 服务。该服务会以异步方式将这些消息传递给已订阅的感兴趣的消费者。
这样一来,服务便可吸收所有寻找对数据感兴趣的消费者的复杂性。该服务还会根据消费者的容量来管理消费者接收数据的速率。这种解耦可让数据生产者以低延迟大规模写入消息,而不会受到消费者行为的影响。
Pub/Sub 可提供高度可伸缩且可靠的消息传送服务。虽然该服务会自动处理大部分此类问题,但您可以控制发布者和订阅者的不同方面,从而影响可用性和性能。本指南的其余部分将详细介绍这些方面。
隔离
默认情况下,Pub/Sub 是一项全球性服务:主题和订阅并非固有地绑定到特定区域,并且消息会在 Pub/Sub 服务中根据需要在区域之间流动。使用全局端点 pubsub.googleapis.com
时,发布者和订阅者会连接到运行 Pub/Sub 的网络最近区域。使用区域级端点(例如 us-central1-pubsub.googleapis.com
)或位置级端点(例如 pubsub.us-central1.rep.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 存在订阅方问题。此问题已通过故障切换到备用区域来解决。
在所有四种选项中,第一种是理想之选。如果没有消息在订阅者连接上流动,则该连接不会产生任何费用。唯一产生的费用是订阅者客户端库的额外实例的占用空间,这可以忽略不计。您还必须注意每个区域内打开的 StreamingPull 连接数配额。
第二种模式的优势在于,由于消息只发布一次,因此 Pub/Sub 费用中没有乘数。不过,代价是,对于某些类型的中断,在中断开始之前发布的消息可能在中断解决后才能使用。存储在不可用区域中的消息可能无法传送给订阅者,无论订阅者连接到哪个区域。在服务中断期间发布到备用区域的消息可能可用。此外,发布者或订阅者可能会遇到一段时间的不可用情况,且错误率会增加。这取决于用于检测中断的方法以及故障转移到备用区域的时间。
无论您选择哪种选项,都要注意这可能会如何与 Pub/Sub 的功能互动。按序传送和仅传送一次均在区域内提供保证。例如,如果您使用故障切换冗余技术,则只有在同一区域中发布的消息才能保证按顺序传送。订阅者可能会先收到发布到回退区域的消息,然后再收到发布到主要区域的消息,即使这些消息是先发布到主要区域的。
微调发布商
无论您选择哪种故障切换选项,都需要在发布商本身内执行一些额外的调整步骤。调整发布者行为可确保在高负载下实现最佳性能。批量处理消息是一种以延迟换取成本降低的方式,但它并不是可靠性方面的问题,因此本文不会介绍。不妨重点关注一些有助于提高可靠性的其他参数,包括重试设置和流量控制设置。
发布可能会因各种原因而失败,包括网络不可用等暂时性原因,或需要用户干预(例如更改权限)的原因。Pub/Sub 客户端库会使用重试设置中指定的参数重试暂时性错误。这些设置控制因暂时性原因而失败的发布 RPC 重试的指数退避算法行为。虽然默认设置通常在大多数情况下都能正常运行,但在某些情况下,您可能需要调整这些值。
您最可能需要调整的两个属性是初始 RPC 超时和总超时。初始 RPC 超时是指第一个发布 RPC 完成的时间。如果任何 RPC 失败或超时,系统会尝试使用更长的超时时间再次执行,直到超出请求总数或总超时时间。
如果发布者受网络限制或距离运行 Pub/Sub 的最近 Google Cloud 数据中心较远,则可以调整初始超时时间。网络限制可能是发布者所运行的机器的吞吐量限制,也可能是同一机器上运行的其他网络密集型服务造成的。如果超时设置得过短,初始 RPC 可能会反复失败,导致需要更多尝试(超时时间更长)才能成功发布。重复需要重试会增加发布延迟时间。在这种情况下,增加初始超时时间可能会加快发布速度。
如果网络连接不稳定,增加总超时时间和初始超时时间可能会有所帮助。增加总超时时间可让发布 RPC 有更多时间成功完成。如果发布 RPC 始终因超出截止期限而失败,请考虑调整这些值。
发布时持续出现“超出截止期限”错误也可能表明需要调整发布者流量控制。借助这些设置,您可以确保发布者能够应对传入流量的峰值,从而生成更多要发送到 Pub/Sub 的消息。出站请求的大幅增加可能会使发布商的 CPU、内存或网络容量过载。当发布过载时,它无法在超时之前处理发布请求或响应。这会导致发布请求更多,最终达到总超时时间。发布商流量控制会限制在未收到发布请求的响应时可以未完成的消息或字节数。通过这种方式限制请求数量,即使在高峰时段也能将资源利用率保持在可管理的水平。根据发布商的运作方式,您可以通过允许发布操作阻止进一步的请求,从而允许后续的发布 RPC 等待容量。或者,您也可以通过让流量控制在达到容量时返回错误,来向服务的调用方施加压力。您可以配置发布者客户端库在超出限制时的响应方式。
微调订阅者
可能还需要调整订阅者,以确保其可靠运行。与发布商类似,您可以调整订阅者的流量控制设置,以确保他们不会不堪重负。订阅者客户端库使用流式拉取,其中客户端会打开与服务器的持久数据流,而服务器会在消息可用时发送消息。如果发布的消息数量大幅增加,订阅者可能会收到超出其处理能力的消息。在流控制到位的情况下,一次性发送到客户端的未确认消息数量会受到限制。这样可以减少同时处理的消息数量,并延长消息的处理时间。分散负载可让订阅者在任何影响消息处理的资源限制范围内保持运行,这可能会导致连锁效应,最终导致无法处理任何消息。
如果您预计要处理的数据量只会出现峰值,最终会回落,那么仅使用流量控制就足够了。如果流量通常会随着时间的推移而增加(因为使用量增加),那么流控制可以保护订阅者。不过,这可能会导致积压的消息不断增加,并导致消息在保留时长到期之前无法传送。在这种情况下,您可能还需要设置自动扩缩,以便在未确认的消息数量不断增加时增加订阅者的数量。具体设置方式取决于您为订阅者使用的计算平台。例如,Compute Engine 的自动扩缩器可让您根据未传送消息数量等指标进行扩缩。同时使用自动扩缩和流量控制功能,可确保订阅者能够应对消息吞吐量的其他短期峰值以及需要更多计算能力的长期增长。请务必遵循将 Pub/Sub 指标用作伸缩信号的最佳实践。
使用快照和搜索进行安全部署
邮件丢失通常是灾难性事件。Pub/Sub 可确保所有发布的消息至少传送一次。不过,这些消息能否得到正确处理取决于订阅者的行为。如果消息已成功确认,Pub/Sub 不会重新传送这些消息。因此,您部署的新订阅者代码中引入的 bug(该 bug 会在未正确处理消息的情况下确认消息)可能会导致订阅者引发的消息丢失。Pub/Sub 提供快照和还原功能,可帮助您确保正确处理每条消息,即使遇到订阅者 bug 也是如此。
每个订阅者部署的模式必须如下所示:

在确定新订阅者是否正常工作之前需要等待的时间长短可能因您的使用情形而异。只有当订阅者被视为正常工作时,才能退出步骤流程,此时可以删除快照。
使用快照和搜索并不意味着可以取代以下最佳实践:先在非生产环境中运行软件,然后逐步部署到生产环境。它们可提供额外的保护,确保可靠地处理数据。但代价是,还原到快照可能会导致已成功处理的消息被重复传送给订阅者。不过,鉴于 Pub/Sub 默认具有“至少一次”传送语义,您的订阅者已经能够应对消息重新传送。