编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

分布式系统中的延迟和成本(分布式低时延金融技术)

wxchong 2024-10-22 18:02:44 开源技术 8 ℃ 0 评论

关键要点

  • 跨越多个可用区域的分布式系统可能会产生大量的数据传输成本和性能瓶颈。
  • 组织可以通过应用区域感知路由技术来降低成本和延迟,而不会牺牲可靠性和高可用性。
  • 区域感知路由是一种通过尽可能将流量引导至同一可用区域内的服务来优化网络成本和延迟的策略。
  • 端到端实现区域感知路由需要各种工具,例如 Istio,并选择支持此功能的分布式数据库。
  • 请注意当您的服务分布不均匀时会出现的问题。处理集群热点并能够在特定区域范围内扩展服务。

微服务架构方法已成为构建成功产品的核心因素。它通过采用服务网格、容器和无服务器计算等先进的云技术而成为可能。快速增长、创建可维护性、弹性和高可用性的需求使团队构建“深度”分布式系统成为标准:具有许多微服务层的系统。跨越多个可用区 (AZ) 甚至区域的系统。这些系统通常以数十个必须通过网络频繁相互通信的聊天微服务为特征。

将我们的资源分布在不同的物理位置(区域和可用区)对于实现弹性和高可用性至关重要。但是,在某些部署中可能会产生大量的数据传输成本和性能瓶颈。本文旨在提高对这些潜在问题的认识,并提供在克服这些挑战的同时保持弹性的指南。

问题

使用多个可用区是最佳实践,也是保持服务可用性的重要因素。无论是微服务、负载均衡器、数据库还是消息队列,我们都必须跨多个可用区配置它们,以确保我们的应用程序在地理上分布,并能承受各种中断。

在一些关键系统中,为了实现容错,将资源和数据分布在多个区域甚至多个云提供商之间也是很常见的。

将我们的服务分布在多个可用区中需要我们在这些可用区之间传输数据。每当两个微服务相互通信或每当服务从分布式数据库读取数据时,通信就可能跨越可用区边界。这是由于负载平衡的性质。

通常,负载均衡器会在上游服务(被调用的服务)的实例之间均匀分配流量,而不会知道源服务位于哪个可用区。因此,实际上,只要在多个可用区上配置系统,跨可用区流量就很可能会大量发生。

但为什么它会成为云成本负担,甚至性能瓶颈呢?让我们来分析一下。


图 1:跨多个可用区域的请求流说明

成本负担

每次在两个可用区之间传输数据时,大型云提供商通常会对跨可用区传入数据传输和传出数据传输收取双向传输费用。例如,如果每个方向的费用为0.01 美元/GB,则两个同一区域可用区之间传输 1TB 数据将花费 10 美元(传出)和 10 美元(传入),总计 20 美元。但是,这些成本在分布式系统上很快就会失控。想象一下,作为请求的一部分,AZ-1 上的负载均衡器实例可能会联系 AZ-2 中的服务 A,后者又调用 AZ-3 上的服务 B,后者又从 AZ-2 上的数据库读取键/值记录,并经常使用来自 AZ-1 上的 Kafka 代理的更新(参见图 1)。

因此,虽然这种架构选择优先考虑可用性,但它很容易出现极端情况,即单个请求的所有服务之间的通信都是跨可用区进行的。因此,现实情况是,在具有各种数据库、消息队列和许多相互通信的微服务的系统中,数据必须频繁移动,并且可能会变得非常昂贵。每次向堆栈添加服务时,负担都会增加。

性能瓶颈

可用区与同一数据中心或区域内的其他可用区在物理上相隔一段有意义的距离,尽管它们之间的距离最多都在几英里之内。这通常会导致可用区之间的往返延迟至少为个位数毫秒,有时甚至更长。回到我们的示例,对可用区 1 上的负载均衡器实例的请求可能会到达可用区 2 中的服务 A,后者又会调用可用区 3 上的服务 B,后者又会从可用区 2 上的数据库中读取键/值记录,并经常使用来自可用区 1 上的 Kafka 代理的更新。这样,我们很容易为每个请求增加十几毫秒的时间。当服务位于同一可用区时,这段宝贵的时间可能会降至亚毫秒级。和成本负担一样,您在堆栈中添加的服务越多,这个负担就越重。

那么,我们如何才能在不牺牲性能的情况下获得跨可用区的可靠性?
我们能做些什么来解决这些额外的数据传输成本?
让我们深入探讨这些问题。

区域感知路由来救援

区域感知路由(又称拓扑感知路由)是解决这些问题的方法。在Unity的Aura中,我们在过去一年中逐步推出此功能。我们发现它在某些系统中节省了 60% 的跨可用区流量。我们了解了如何利用它来优化性能并显著降低带宽成本。我们还发现了它不适用的地方,应该避免使用。那么,让我们来描述区域感知路由以及充分利用它需要考虑哪些因素。

什么是区域感知路由?

区域感知路由是一种策略,旨在通过尽可能将流量引导至同一可用区内的服务来优化网络成本和延迟。它最大限度地减少了跨区域数据传输的需求,从而降低了相关成本和延迟。

区域感知路由旨在通过本地区域将原始服务的全部或大部分流量发送到上游服务。理想情况下,通过本地区域进行路由或执行跨区域路由应取决于原始服务本地区域中上游服务的健康实例百分比。此外,如果当前可用区中的上游服务发生故障或出现故障,区域感知路由组件应能够自动重新路由可用区内的请求,以保持高可用性和容错能力。

从本质上讲,区域感知路由器应充当智能负载均衡器。在尝试保持对区域局部性的感知的同时,它还负责在服务的所有上游实例之间平衡每秒相同数量的请求。其目的是避免特定实例受到流量轰炸的情况。这种流量偏差可能导致局部过载或利用不足,从而导致效率低下并可能产生额外成本。

由于这种特性,区域感知路由在区域流量和资源分布均匀的环境中非常有用(例如,当您将流量以 50/50 的比例分布在两个具有相同资源量的可用区上时)。但是,对于不均匀的区域分布,由于需要平衡多个区域的流量并克服热点(轰炸和过载实例 - 参见图 2),它可能变得不那么有效。

图 2:不均匀区域分布图。两个区域有两项服务。当服务 B 满足来自 AZ-2 的所有请求时,它能否坚持下去?

如何采用区域感知路由?

区域感知路由的概念有多种实现方式。因此,采用它取决于找到适合您的设置和技术堆栈的实现方式。让我们回顾一下其中一些选项:

Istio 本地负载均衡

Istio是一个开源的 Kubernetes 服务网格平台。Istio 可能拥有目前最有前途的区域感知路由实现:本地负载平衡。它是 Istio 中的一项功能,允许根据服务的本地性和上游服务实例,将请求有效地路由到服务的最近可用实例。在 Kubernetes 集群中安装和配置 Istio 时,可以根据地理区域、可用区域和子区域因素定义本地性。

Istio 的本地负载平衡使请求能够优先路由到与源服务位于同一位置(即区域)的上游服务实例。如果与源相同的位置没有可用的健康服务实例,则 Istio 可以进行故障转移并将请求重新路由到最近的可用位置(例如,另一个区域甚至另一个地区)的实例。这确保了高可用性和容错能力。

此外,Istio 还允许您为不同的区域定义权重,使您能够根据容量或优先级等因素控制跨可用区的流量分配。这样,您就可以调整每个区域中保持本地化的流量量与跨区域发送的流量量,从而更轻松地克服不均匀的区域分布。

拓扑感知路由(又称拓扑感知提示)

如果您在没有 Istio 的情况下使用 Kubernetes,拓扑感知路由是 Kubernetes 的一项原生功能,在版本 1.17 中引入(作为拓扑感知提示)。虽然它比 Istio 提供的功能稍微简单一些,但它允许 Kubernetes 调度程序根据集群的拓扑做出智能路由决策。它考虑拓扑信息,例如节点的地理位置(例如区域)或可用区域,以优化 Pod 的放置和流量的路由。

Kubernetes 还围绕此功能提供了一些必要的保护措施。Kubernetes 控制平面将在使用该功能之前应用这些保护规则。如果这些规则不成立,Kubernetes 将不会本地化请求。相反,它将选择一个实例,而不管区域或地区如何,以保持高可用性并避免特定区域或实例过载。这些规则可以评估每个区域是否都有可用的服务实例,或者是否确实有可能在区域之间实现平衡分配,从而防止在集群上产生不均匀的流量分布。

虽然拓扑感知路由能够优化跨区域流量并处理使用多个可用区的隐藏性能负担和成本,但它的功能不如 Istio 的局部负载平衡。主要缺点是它不能像 Istio 那样处理故障转移。相反,它的保护措施方法会完全关闭服务的区域局部性,这是一种更为严格的设计选择,要求服务均匀分布在各个区域才能从这些规则中受益。

端到端区域感知路由

使用 Istio 的本地负载平衡或 Kubernetes 拓扑感知路由来维护区域感知路由是解决分布式系统数据传输成本和性能瓶颈的重要一步。但是,要端到端采用区域感知路由并将跨可用区的数据传输降至最低,我们还必须确定我们的服务是否能够通过本地区域从数据库和消息队列 (MQ) 读取数据(见图 3)。

图 3:端到端单个可用区上的请求流图示

DB 和 MQ 通常分布在多个可用区以实现高可用性,在每个可用区中维护每条数据的副本。这就是为什么 DB 和 MQ 容易被跨区域访问,从而使系统承受性能和成本负担。那么,我们能否通过本地区域访问数据来优化 DB 读取延迟并降低数据传输成本,同时又不影响弹性?

以下是一些可以支持区域感知路由的 DB 和 MQ 的示例:

Kafka 的关注者获取功能

Apache Kafka是一个开源分布式事件流平台,用于高性能数据管道、流分析和数据集成。与其他 MQ 一样,它通常用于连接微服务并在分布式系统之间移动数据。
追随者获取是 Kafka 客户端库的一项功能,允许消费者优先从本地可用区域读取数据,无论是从领导者节点还是副本节点读取。此功能基于 Kafka 的机架感知功能,旨在通过利用数据中心的物理或逻辑拓扑来增强数据可靠性和可用性。

Kafka 的机架感知要求集群中的每个代理都分配有相应的机架信息(例如,其可用区域 ID),这样就可以确保主题分区的副本分布在不同的可用区中,从而在可用区或节点发生故障时将数据丢失或服务中断的风险降至最低。虽然机架感知并不直接控制客户端读取的位置,但它确实允许 Kafka 代理通过机架标签提供其位置信息,以便客户端库可以使用它来尝试从位于同一机架或区域中的副本读取,或者在本地代理不可用时回退到另一个区域。因此,它遵循区域感知路由的概念,可优化数据传输成本和延迟。要利用追随者获取,我们必须为消费者分配机架标签,指示它当前正在哪个可用区运行,以便客户端库可以使用它来定位同一可用区上的代理。

请注意,当使用 Kafka 的跟随者获取功能并且本地代理的复制落后于领导者时,它将增加消费者在该本地区域内的等待时间,而不是影响跨区域的随机消费者,从而将某些问题的波及范围限制在本地区域。此外,与其他区域感知实现一样,它对消费者的区域分布不均匀非常敏感,这可能会导致某些代理过载并要求消费者处理故障转移。

Redis 和其他区域感知数据库

Redis是分布式系统中常用的键值数据库。它是用于高吞吐量、大规模应用程序的数据库之一,用于缓存和其他亚毫秒级查询。出于冗余原因,该数据库通常会分布在多个可用区中。因此,任何从 Redis 读取数据的地理分布应用程序都会比在本地区域读取数据具有出色的性能和成本效益。

Redis 没有内置支持根据客户端的可用区域自动将读取请求路由到本地副本。但是,我们可以使用某些 Redis 客户端库获得此功能。例如,当使用Lettuce(一个 Java 库)时,将客户端的“ReadFrom”设置设置为 LOWEST_LATENCY 将配置客户端从数据延迟最低的副本读取,无论它是在副本还是主节点上。此副本通常驻留在本地区域中,因此减少了跨区域数据传输。

如果您使用的客户端库不支持选择最近的副本,则可以通过从本地 Redis 端点检索数据来实现自定义逻辑。并且最好在需要时回退到跨可用区端点。

还有更多流行的数据库技术支持区域感知路由;以下两种值得一提:

Aerospike — 专为高性能数据操作而设计的分布式键值数据库。它具有机架感知功能,与 Kafka 类似,它为数据库客户端提供了一种机制,使其优先从最近的机架或区域读取数据。

Vitess——最初由 Youtube 开发的 MySQL 的分布式可扩展版本,通过其本地拓扑服务实现区域感知路由,使其能够通过本地区域路由查询。

虽然我们可能会注意到,区域感知路由的概念在每项技术中都有略微不同的名称,但所有这些实现都有相同的目标 - 在不牺牲服务可靠性的情况下改善读取延迟和数据传输成本。

将我们的 DB 和 MQ 分布在多个可用区的另一个成本负担是,我们写入的每块数据都需要在 DB 集群本身内复制到每个区域。复制本身可能会导致大量的数据传输成本。虽然从技术上讲这无可奈何,但必须注意的是,通常托管的 DB 服务(例如 AWS MSK(亚马逊的 Apache Kafka 托管流)、AWS RDS(亚马逊关系数据库服务)、Elasticache(亚马逊的托管 Redis)等)不会向用户收取集群内跨可用区数据传输的费用,而只会收取进出集群的数据的费用。因此,如果您计划复制大量数据,选择托管服务可能会成为一个重要的成本考虑因素。

处理热点和不均匀的服务分布

虽然强烈建议我们保持服务在各个区域均匀分布,但这并不总是那么简单或可能。处理热点是一个复杂的话题,但解决该问题的一种方法是考虑为每个区域单独部署和单独自动扩展策略。允许服务根据特定区域内的流量独立扩展可以确保如果负载集中在特定区域,它将得到充分扩展以使用专用资源处理该负载,从而无需回退到其他区域。

结论

在高可用性分布式系统中应对数据传输成本和性能是一项真正的挑战。要解决这个问题,我们首先需要找出哪些微服务容易发生频繁的跨可用区数据传输。我们需要以数据为导向,衡量跨可用区成本,并跟踪微服务通信的延迟,以确保我们的努力方向正确。

然后,我们必须采用适当的工具来消除或最小化这些跨可用区调用。这些工具和优化取决于我们的技术堆栈。要端到端采用区域感知路由,我们必须针对不同的用例采用不同的工具。某些工具(如 Istio 和拓扑感知路由规则)旨在为相互通信的 Kubernetes pod 解锁区域感知。但是,它们不适用于您的微服务从数据库或消息队列跨可用区使用数据的情况。因此,我们必须选择正确的 DB 和 MQ,并在其客户端库中嵌入区域感知路由。

最后,我们必须确保不会因为在不均匀的区域分布设置中部署这些工具和优化而破坏系统的高可用性和弹性,因为这样会导致热点(实例受到流量轰炸)。这样的部署可能会违背目的,导致我们通过创建新问题来解决成本和性能问题。

总体而言,优化跨可用区流量至关重要;它会影响我们应用程序的性能、延迟和成本,但必须谨慎处理以确保我们不会牺牲应用程序的可靠性和高可用性。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表