本指南将为您介绍 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 将消息同步写入一个区域内的多个可用区。因此,地区服务中断不足以阻止消息传送;整个区域都必须不可用。如果 Pub/Sub 在发布者发送消息的区域不可用,则在服务完全恢复之前,该区域中的消息可能不会被传送:

消息最终仍会最终传送(假设消息保留期限尚未过),并会因服务中断的时长而延迟。请注意,与订阅者类似,美国的发布者也不会在服务发生故障时故障切换到其他区域。此行为有助于防止因发布者或订阅程序故障而导致跨区域级联故障。
隔离
详细的默认故障切换语义会影响数据隔离,以及发布者、订阅者或 Pub/Sub 本身不可用对消息流的影响。您的用例可能需要不同级别的隔离。例如,您可以要求所有邮件在区域内递送。
如果您不希望隔离,那么详细的默认故障切换语义就足够了。您必须创建一个主题和单个订阅,并将发布者和订阅者置于所有选定的区域中。如果订阅者不可用或 Pub/Sub 在他们所连接的区域发生故障,传送操作会故障给其他区域中的订阅者。
对于区域隔离(保证数据不会离开某个区域),请创建主题和订阅以处理每个区域中的消息。在每个区域找到发布者和订阅者,并让他们分别发布和订阅相应的区域主题和订阅。您还必须使用区域端点来确保数据仅在区域内移动。如果单个区域中发布者、订阅者或 Pub/Sub 发生故障,消息传送会在该区域中停止。其他区域的主题和订阅的消息传送不受影响。
最后,在 Pub/Sub 中不可能实现地区隔离(保证数据保留在单个地区内)。如果您需要让各个区域保持独立,请使用 Pub/Sub Lite。
由客户控制的故障切换和冗余
Pub/Sub 的默认故障切换语义可能无法完全保证,如果中间发生服务中断,消息始终可以从发布者流向订阅者。服务中断可能发生在多个不同位置,包括客户端、发布者或订阅者运行的服务中、网络中,甚至 Pub/Sub 本身中很少见。如果您需要服务能够灵活应对此类服务中断,则必须实现自己的冗余。通常,这些冗余包括使用多个发布者和订阅客户端实例,其中每个实例使用不同的位置端点。
您可能需要对两个不同的影响范围具有弹性:可用区级或区域级。以下是针对各个广告系列的设置选项。
可用区弹性
Pub/Sub 内置了跨可用区复制功能。您无需采取任何特殊步骤来处理影响服务本身的单区域服务中断。但是,要使客户端或网络能够从中断中恢复,最好在该区域内的多个区域中以足够的容量来运行发布者和订阅者。如果一个可用区出现故障,另一个可用区中的客户端就能够接收流量并处理消息。最佳做法是不要同时向这些客户端发布更改,以便在引入 bug 时,其他未受影响的区域可以继续处理消息。
区域弹性
为了能够应对区域故障,请在发布者和订阅者中设置额外的冗余。您可以在多个区域运行发布者和订阅者,以处理这些客户端或网络中出现中断的可能性。
如果您希望对某个区域中潜在的 Pub/Sub 故障具有弹性,则必须具备可应对此类中断的故障切换机制。可能的方法是在端到端消息传送延迟时间和费用之间权衡取舍。
在不太在意费用的情况下,为了最大限度地缩短延迟时间,最好的策略是始终在不同区域同时发布和订阅。首先,选择您希望实现冗余的区域数量。 接下来,您可以为每个区域设置主题和订阅(尽管不是绝对必要)。
每个发布者创建与区域数量一样多的发布者客户端(每个区域一个),并使用不同的位置端点来确保将消息定向到不同的区域。如果使用单独的主题,则每个发布者客户端都必须发布到每个区域的相应主题。对于每条消息,发布者在每个客户端上都调用 publish。使用冗余发布时,如果任意一个发布失败,则无需重试发布。
同样,每个订阅者会创建这么多个订阅者客户端(每个区域一个),并使用位置端点连接到不同的区域。如果对每个区域使用不同的订阅,则每个订阅者客户端必须使用相应的订阅。请注意,发布者和订阅者使用的区域不一定相同。订阅者跨三个订阅接收消息并处理它们。
此设置有几个关键功能和要求:
- 任何单区域服务中断都不会影响对已发布消息的处理,也不会影响在服务中断期间发布的消息的处理。由于消息发布到了多个区域,因此如果其中一个区域发生故障,则消息在其他区域仍然可用。在服务中断期间,发布调用在受影响的区域中失败,但在其他区域中成功。
- 只要消息流动的任何区域可用,消息处理延迟时间就不会受到影响。
- 消息处理必须具有幂等性。由于每条消息将进行多次传送,因此消息处理必须灵活应对重复。发生地区性服务中断时,其中一些重复项可能会比首次传送消息的时间晚得多。这些重复项可能来自未遇到服务中断的其他区域。
使用这种冗余运行可为任何种类的服务中断提供最高的弹性。对于依赖于 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 为发布的所有消息提供至少一次传送。但是,这些消息的正确处理方式取决于订阅者的行为。如果消息成功确认,Pub/Sub 不会重新提交这些消息。因此,如果您部署的新订阅者代码中引入一个错误,该错误会在没有正确处理消息的情况下确认消息,则可能会导致订阅者导致的消息丢失。Pub/Sub 提供快照和还原功能,可帮助您确保正确处理每条消息,即使遇到订阅者错误也是如此。
每个订阅者部署的模式必须如下所示:

确定新订阅者是否正常工作之前需要等待的时间可能会因您的用例而异。退出步骤流程的唯一方法是当订阅者被视为正在运行时,此时可以删除快照。
使用快照和还原功能并不意味着取代有关在非生产环境中首次运行软件以及逐步部署到生产环境的最佳实践。它们提供额外的保护,以确保数据的可靠处理。需要权衡的是,跳转至快照可能会导致订阅者成功处理的消息重复传送。但是,由于 Pub/Sub 默认具有“至少一次传送”语义,因此订阅者在重新传送消息方面已经具有弹性。