本指南介绍了 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 精简版。
客户控制的故障切换和冗余
Pub/Sub 的默认故障切换语义可能无法完全保证,如果发布端和订阅端之间发生中断,消息始终能从发布端流向订阅端。中断可能会发生在多个不同位置,包括客户端、发布方或订阅方运行的服务、网络,甚至极少数情况下会发生在 Pub/Sub 本身。如果您需要自己的服务能够应对此类服务中断,则必须自行实现冗余。通常,这些冗余包括使用多个发布方和订阅方客户端实例,每个实例使用不同的位置端点。
您可能希望针对两种不同影响范围(可用区级或区域级)实现弹性。以下是每种方式的设置选项。
可用区级弹性
Pub/Sub 具有内置的跨可用区复制功能。您无需采取任何特殊措施即可处理影响服务本身的单个可用区中断。不过,为了让客户端或网络能够抵御服务中断,最好在区域内的多个可用区中运行具有足够容量的发布端和订阅端。如果单个可用区发生故障,则另一个可用区中的客户端能够接收流量并处理消息。最佳实践是不要同时向这些客户端发布更改,以便在引入 bug 时,其他未受影响的可用区可以继续处理消息。
区域弹性
为了应对区域性故障,请在发布端和订阅端设置额外的冗余。您可以在多个区域运行发布方和订阅方,以应对这些客户端或网络中断的可能性。
如果您希望能够应对某个区域可能发生的 Pub/Sub 故障,则必须准备好故障切换机制来处理此类服务中断。可行的方法是在端到端消息传送延迟时间和费用之间进行权衡。
如果不考虑费用,为了最大限度地缩短延迟时间,最佳策略是始终在不同区域同时发布和订阅。首先,选择您希望在多少个区域中实现冗余。 接下来,您可以为每个区域设置一个主题和订阅,但这并非绝对必要。
每个发布商都会创建与区域数量相同的发布商客户端(每个区域一个),并使用不同的位置端点,以确保消息会定向到不同的区域。如果使用单独的主题,每个发布商客户端都必须发布到每个区域对应的主题。对于每条消息,发布方都会对每个客户端调用 publish。使用冗余发布功能后,如果其中任何一次发布失败,则无需重试发布。
同样,每个订阅方都会创建这么多订阅方客户端(每个区域一个),并使用位置端点连接到其他区域。如果为每个区域使用不同的订阅,则每个订阅方客户端都必须使用相应的订阅。请注意,发布商和订阅者使用的区域不一定相同。订阅者接收这三个订阅中的消息并进行处理。
此设置具有以下几个关键特性和要求:
- 任何单个区域服务中断都不会影响对已发布消息的处理,也不会影响在服务中断期间发布的消息。由于消息已发布到多个区域,因此即使某个区域发生故障,消息仍可在其他区域中使用。在服务中断期间,发布调用会在受影响的区域失败,但在其他区域会成功。
- 只要消息流经的任何区域可用,消息处理延迟时间就不会受到影响。
- 消息处理必须具有幂等性。由于每条消息都会多次传送,因此消息处理必须能够抵御重复。如果某个地区发生服务中断,则其中一些重复消息可能比首次传送消息的时间要晚得多。这些重复数据可能来自未发生中断的其他区域。
采用这种冗余配置可最大限度提高对任何类型中断的弹性。对于依赖于 Pub/Sub 且需要最高可用性的 Google 内部服务,此设置是首选。不过,这种设置会带来一个权衡:消息传送费用需要乘以所用区域的数量。对于必须跨区域传送的消息,还会产生额外的区域间网络使用费用。
冗余的另一种方法是,仅在请求失败或消息未按预期从发布端流向订阅端时进行故障转移。在这种情况下,您有一个主要区域,您会通过位置端点将发布商和订阅者定向到该区域。与之前一样,这些区域不必相同。这样,您还可以为发布商和订阅者设置回退区域,以便在主区域不可用时使用。
只有在请求成功发送后,发布商才会将其内容发布到主要区域(通过位置端点)。每当系统确定某个区域处于停用状态时,发布商都会改为向后备区域发布。您可以通过两种方式确定某个区域处于停机状态并发生故障转移。这可以通过手动流程完成,配置会在发布商中动态更新。如果发布请求中的错误率足够高,发布商也可以自行更新配置。
订阅者必须始终通过位置端点连接到主区域。您可以通过以下一个或多个触发器决定订阅者是否可以使用回退区域:
- 始终订阅回退区域。在这种情况下,订阅方始终与主区域和回退区域保持连接。发布商和订阅者都可以为主要区域和后备区域使用相同的区域。如果是这种情况,则只有在发布方发生故障转移时,订阅方才必须通过备用区域接收消息。
- 通过配置手动检测订阅者并将其切换到回退区域。如果您检测到服务中断,可以故障切换到回退区域,然后在服务中断问题得到解决后再移回主区域。
- 在出现订阅方错误时进行故障转移。如果订阅方请求返回错误,您可以将其视为必须故障切换到回退区域的信号。请注意,Pub/Sub 客户端库会在出现暂时性错误时在内部重试流式拉取请求,因此您可能无法检测到长时间出现意外错误的情况。此外,即使在正常运行期间,流式拉取错误率也应为 100%。
- 如果订阅者在意外长时间内未收到消息,则进行故障转移。假设消息发布是一致的,订阅者可以随时接收消息。如果订阅方长时间未收到任何消息,则主要区域的 Pub/Sub 中可能存在订阅端问题。通过回退到回退区域可以解决此问题。
在所有四个选项中,第一个选项是理想之选。如果订阅者连接没有流通消息,则不会产生任何费用。唯一的开销是订阅者客户端库的额外实例的占用空间,这可以忽略不计。您还必须注意每个区域内打开的 StreamingPull 连接数配额。
第二种模型的优势在于,由于消息只发布一次,因此 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 提供快照和跳转功能,可帮助您确保正确处理每条消息,即使在出现订阅器 bug 时也是如此。
每个订阅方部署的模式都必须如下所示:
在确定新订阅者是否有效之前等待的时间长短可能会因您的用例而异。只有在订阅者被视为正常运行时,才能退出步骤流程,届时可以删除快照。
使用快照和跳转并不会取代先在非生产环境中运行软件,然后逐步部署到生产环境的最佳实践。它们可提供额外的保护,以确保可靠地处理数据。但代价是,如果还原到快照,可能会导致订阅者成功处理的消息重复传送。不过,鉴于 Pub/Sub 默认采用“至少一次”传送语义,您的订阅者已经能够应对消息重新传送。