为什么Uber微服务架构使用多租户

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介:
云栖号资讯:【 点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

Uber服务的高性能主要依赖于在当前平台上快速以及稳定的开发新特性能力,和对应服务使用什么技术栈无关。Uber平台最根本的能力是基于微服务架构,这是一种常用的结构化风格,也就是由各种互操作的服务组成的应用。

微服务架构可以提供很好的伸缩性,同时也能够支持稳定的部署和模块化。在Uber,不同的工程师团队都是基于互操作的服务来工作,所以确保我们的技术栈既要能够安全的发布新的变动,也要能够基于模块化方式可靠的使用架构的已有部分,这点非常重要。总而言之,这些功能能够提高开发者的开发速度以及快速的发布周转(turnaround)次数,另外还可以给我们提供基于独立调度(schedules)构建的灵活性,同时依然可以满足服务级别协议(SLAs)。

在一个微服务架构中允许多系统共存是利用微服务稳定性以及模块化最有效的方式之一,这种方式一般被称为多租户(multi-tenancy)。租户可以是测试,金丝雀发布,影子系统(shadow systems),甚至服务层或者产品线,使用租户能够保证代码的隔离性并且能够基于流量租户做路由决策。对于传输中的数据(data-in-flight)(例如,消息队列中的请求或者消息)以及静态数据(data-at-rest)(例如,存储或者持久化缓存),租户都能够保证隔离性和公平性,以及基于租户的路由机会。多租户可以帮助我们在一个简单的微服务栈上实现多种功能,比如改进的集成测试框架,影子流量路由,记录和重放流量,为了实验功能做的实时流量的密封重放,容量规划,实际的性能测试,甚至一次运行多个产品线。

综上所述,多租户的好处是利用了一个更灵活和可伸缩的微服务架构,这个架构能够带来更大的生产效能以及改进的应用程序性能,这些会让工程师以及平台用户这类人受益。

微服务架构(landscape)

微服务架构能够让开发团队不依赖于其他服务就可以推出新的功能以及bug修复,这可以增加开发者的开发速度。例如,一个团队拥有四个服务(作为一个整体称为系统1),这些服务具有约定的SLA,这些约定的SLA定期与具有其自己的SLA的多个其他服务交互。

下面的图表1说明了这四个服务,A,B,C和D之间的交互。在这个图表中,服务A从系统2中接收请求。系统1通过连接到服务B来处理请求,而服务B去连接服务C和服务D,最终完成请求处理。

1

图表1。在系统1中,服务A通过连接到服务B来处理系统2的请求,而服务B去连接服务C和服务D,最终完成请求处理

在这个例子中,如果我们对服务B做出改变,我们需要确保它仍然能够和服务A,C,D正常交互。在微服务架构中,我们需要做这些集成测试场景,也就是测试和该系统中其他服务的交互。通常来说,微服务架构有两种基本的集成测试方式:并行测试和生产环境测试。

并行测试

并行测试需要一个和生产环境一样的过渡(staging)环境,并且只是用来处理测试流量。如下图表2所示,这个环境栈一直启动并且运行生产环境代码,但是和生产环境栈是隔离的,并且它的规模要比生产环境要小:

2

图表2。并行测试需要工程师创建一个过渡环境来处理测试流量并且考察生产环境栈最终是否满足SLA

在并行测试中,工程师团队首先完成生产服务的一次变动,然后将变动的代码部署到测试栈。这种方法可以在不影响生产环境的情况下让开发者稳定的测试服务,同时能够在发布前更容易的识别和控制bug。

并行测试需要确保测试流量不能够泄漏到生产环境栈,这个可以通过物理隔离方式给它们分配独立的网络以及确保测试工具仅仅在测试栈中操作。

尽管并行测试是一种非常有效的集成测试方法,但是它也带来了一些可能影响微服务架构成功的挑战:

  • 额外的硬件成本:需要给测试提供整个栈,以及所有的数据存储,消息队列和其他基础组件,这意味着需要额外的硬件和维护成本。
  • 同步问题:测试栈只有在和相应的生产栈保持一致才有作用。当两个栈有偏差时,测试栈对于生产栈的反映(mirror)变得越来越困难,并且对于基础组件来说,要保持两个栈的同步也需要额外的负担。
  • 不可靠性测试:当团队把他们实验性的并且有潜在bug的代码部署到测试栈,这些服务可能无法正常的运行,这就会导致测试失败。例如,拥有服务A的团队执行了并行测试去查看他们的新代码是否正常,但事实由于服务B存在一个bug导致测试失败了。因为我们测试的构建和生产环境的完全不一样,这会让我们很难定位bug;此外,我们只有在测试通过整个流程之后才会知道对于服务A做的变动是否是安全的,这意味着我们需要等待拥有服务B的团队把他们干净的代码部署回测试栈。可以通过使用路由框架将流量路由到另一个沙盒环境(在该沙盒环境中待测试的服务已经启动)来缓解这一特殊缺点。
  • 不精确的负载容量(capacity)测试:为了评估整个栈或者子栈的负载容量,我们需要在测试栈上运行测试负载。如果要测试特定容量,则必须先增加测试栈的容量,然后才能将增量负载(即目标容量相对于当前生产环境负载的增加)施加到测试栈上。此增量负载可能无法使测试栈饱和,这会导致我们不清楚应向生产栈中增加多少容量以实现目标容量。

生产环境测试

微服务架构中的另一种集成测试方法是使生产栈支持多租户并且允许测试流量和生产流量流入。下图3就是这样的一个例子:

3

图表3。利用多租户生产栈,我们可以在运行生产服务的同时测试新增或者更新的微服务。

这种方法很有野心,因为这需要确保栈中的每一个服务要能够处理生产请求以及测试请求。

使用这种方法,我们可以把待测试的服务B在一个隔离的沙盒环境中启动,并且在沙盒环境下可以访问生产服务C和D。我们把测试流量路由到服务B,同时保持生产流量正常流入到生产服务。服务B仅仅处理测试流量而不处理生产流量。另外要确保生产流量不要被测试流量影响。

上面的例子只是一个简化的视角,但是却能够很好的解释多租户怎样解决集成测试的问题。生产中的测试提出了两个基本要求,它们也构成了多租户体系结构的基础:

  • 流量路由:能够基于流入栈中的流量类型做路由。
  • 隔离性:能够可靠的隔离测试和生产中的资源,这样可以保证对于关键业务微服务没有副作用。

这里的隔离要求特别广泛,因为我们希望隔离所有可能的静态数据,包括配置,日志,指标,存储(私有或公共)和消息队列。这种隔离要求不仅仅作用于待测试的服务,对于整个栈也是一样。

除了集成测试意外,多租户也为其他用例铺平了道路,例如分阶段部署以及流量重放。

金丝雀部署

当开发者对他们的服务做了变动,即使这个变动已经被严格的审查和测试,我们也不能够一次就把变动部署到所有运行的服务实例上。这是为了确保在变动有问题或者bug的情况下,整个用户群不会受到攻击。理想的做法是首先将变动应用到一小部分实例上,这被称为金丝雀。然后,我们使用反馈回路监视金丝雀,并逐步应用代码变动到所有服务实例上。

在多租户架构中金丝雀可以被视为另一种租户形式,并且可以把金丝雀作为请求中的一个属性来做路由选择。使用金丝雀的时候,资源在部署中也是隔离的。在任何给定时间,一个服务都有可能部署了金丝雀,并且所有金丝雀流量都会路由给已经部署的金丝雀。可以在靠近架构的边缘基于请求自身的属性来采样金丝雀请求,例如用户类型,产品类型,以及用户位置。

捕获/重放和影子流量

对于所做变动的安全性,一个行之有效的测量方法是在为实际的生产流量服务的同时,能够看到服务费用(service fare)发生怎样的变化。在一个封闭的安全环境中重放之前捕获的实时流量或者重放实时生产流量的一份影子拷贝是另一种多租户的用例。

4

图表4。创建影子流量到测试服务涉及到把生产流量的拷贝路由出生产栈并且路由进一个安全的测试环境。

在这个案例中,我们对被测试实例所有出站调用都添加了响应。捕获和重放生产流量可以被视为集成测试的一个子类别,因为这些用例都属于测试和实验范围。

从技术上说,重放流量是测试流量并且可以是测试租户的一部分,允许和其他租户隔离。通过重放流量,我们可以灵活地分配单独的租户,用来进一步隔离其他测试流量。

因此,多租户体系架构的一个重要功能是其保护和隔离多个关键业务产品线或不同用户群的能力。

面向租户的架构

在面向租户的微服务体系架构中,租户被视为一等对象(first class object)。传输中的数据和静态数据都有租户的概念。做一个多租户微服务架构涉及到给入站请求绑定上下文(context),然后在请求的生命周期内把上线文传递下去,这样可以让用户基于上下文做路由。

租户上下文

由于微服务架构是在互连的网络上运行的一组完全不同的服务,因此我们需要能够将租户上下文附加到执行序列上。当请求进入边缘网关时,我们可以将包含了租户相关信息的上下文附加到请求上。我们希望该上下文的生命周期要和被附加上的请求相同,并能够传播到同一业务逻辑上下文中生成的任何新请求中,从而保留请求序列的租户信息。

下面是一个简单的租户上下文格式和一些案例:

5

上下文传播

通常来说,当调用链中的任何一个服务接收到一个请求时,我们都希望能够获得租户上下文信息,因为这个服务可能会利用租户上下文信息作为业务逻辑的一部分。但是,这就要求服务在处理该请求时并且需要进一步发出其他请求时能够传播上下文。

大部分服务一般都不需要租户上下文信息,但是有一些需要访问请求中的上下文用来避开一些业务逻辑。例如,需要验证用户手机号的审计服务可能需要避开对测试流量的检测,因为测试请求中的用户都是测试用户。另外,当测试流量通过一个事务处理服务,这个服务和银行网关对接用来处理用户的转账业务,我们可以屏蔽这个银行网关或者和银行的测试网关通信(如果有用于测试的网关),这样可以避免真实的转账。租户上下文的传播可以通过开源工具来实现,比如OpenTracing和Jaeger,这些可以使用一种语言并且跨传输层方式来实现分布式上下文传播。

租户上下文也应该可以被传播到其他传输中的数据对象中,比如Kafka消息队列中的消息。 较新版本的Kafka支持添加标头,并且可以使用开源跟踪工具向消息添加上下文。

我们也希望租户上下文能够被传播到静态数据中,包括各个服务用来存储持久化数据的所有数据存储系统,比如MySQL,Apache Cassandra以及AWS。像Redis和Memcached这样的分布式缓存也可以被归类为静态数据。架构中使用的所有存储系统以及缓存都需要支持存储上下文以及一个合理的数据粒度,这样才能够基于租户上下文来对数据进行查询和存储。在一个更高的维度上,静态数据组件要求能够基于租户信息来对数据和流量进行隔离。

究竟如何隔离数据以及如何将租户上下文与数据一起存储,这些实现细节都和特定存储系统有关。

基于租户的路由

一旦我们能够给请求附上租户信息,我们就可以基于它的租户信息做路由。这种路由对于生产,记录/重放以及影子流量的测试很重要。当然,金丝雀部署也需要把金丝雀请求路由到运行在隔离环境下的特定服务实例。

在确定无缝运行且无开销的路由解决方案时,考虑部署和服务技术当栈非常重要。当选择一个通用的路由方案时,服务编写的语言以及他们之间互相通信的传输和编码也需要考虑。像Envoy或者Istio开源服务网格(Service Mesh)工具也非常适合提供基于租户的路由功能,并且该路由和服务语言以及所使用的传输或编码无关。

通常,可以在服务的出口或入口实现基于租户的路由。在出口处,服务发现层可以根据请求的租户信息决定和什么服务通信。另一种方法是在入口处做路由判断,然后请求被重新路由到正确的实例,如下图5所示:

6

图5。我们可以在入口处做路由判断,上面的例子是从生产环境服务Ap发送测试流量到测试实例A1。

在图5例子中,可以使用一个边车(sidecar)来转发一个测试请求到测试实例上。边车可以是一个代理进程,它负责代理所有进入该服务的流量,并且和服务部署在一起。流量先是被服务边车接收到,然后边车检测请求的租户上下文,接着基于上下文做出路由决策。

基于我们想要的用例,我们可以在租户上下文中增加额外的元数据。例如,对于生产中的测试,我们想把测试流量重定向到一个服务的测试实例上。我们可以在上下文中增加额外的信息,这个可以允许以下的行为发生:

7

当做路由决策时,我们可以检测请求的租户信息是否是测试的以及请求的接收者是否是处于测试下。如果这些请求都满足,我们可以路由该请求到这个<测试实例ID>。

数据隔离

我们想去构建一个架构,在这个架构中每一个基础组件都能够理解租户信息,并且能够基于租户路由隔离流量,同时在我们的平台中允许对运行不同的微服务有更多的控制,比如指标和日志。在微服务架构中典型的基础组件是日志,指标,存储,消息队列,缓存以及配置。基于租户信息隔离数据需要分别处理基础组件。例如,我们可以生成租户信息,然后将其作为服务生成的所有日志和指标的一部分。这个可以帮助开发者基于租户信息做过滤,同时也可能有助于避免错误警报或防止启发式或训练数据出现偏差。

同样的,当考虑存储服务时,底层的存储架构需要考虑能够有效的在租户之间创建隔离。一些存储架构很容易实现多租户。两种高级方法是将租户概念(notion)显式地嵌入到数据旁边,并将不同的租户信息和数据共同放置,或者根据租户信息显式地分离数据,如下图6所示:

8

图6。使用租户信息路由数据和消息队列测试流量到处于测试下的不同组件,这样可以隔离该测试流量,因此不会干扰生产系统。

后一种方法提供了更好的隔离保证,而前一种方法通常需要较少的操作开销。对于像Kafka这种消息队列系统,我们可以给租户推出一个新的主题或者分配一个单独的Kafka集群。

对于数据隔离,上下文需要能够被传播到基础组件。确保服务在数据隔离方面的开销最小,这一点很重要。理想的情况下,我们希望服务不需要显示的处理租户信息。另外,我们也希望将隔离逻辑放置在所有数据流经的中央扼流点。网关就是其中一种可以实现隔离逻辑的扼流点,它是我们的首选方法。客户端库可以是实现基于租户隔离的另一种方法,尽管编码语言的多样性使在所有特定语言的客户端库之间保持逻辑同步变得有点困难。

同样的,对于配置隔离,我们希望对于特定租户下的服务配置是互相独立的,因此确保对于一个租户配置的变动不会影响到其他租户的配置。

展望未来

基于微服务的体系结构仍在发展,并已成为开发人员和整个组织的敏捷性工具促进者(facilitators)。 精心计划的多租户架构可以提高开发人员的生产力并支持不断发展的业务线。

Uber的多租户实施带来了各种好处,例如使代码和配置的自动发布更安全,从而提高了开发人员的速度。 多租户架构的隔离保证使Uber可以出于各种目的(包括测试流量)重新利用同一微服务栈。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址: https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

相关文章
|
18天前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
17天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
21天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
79 6
|
21天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
32 1
|
17天前
|
Dubbo Java 应用服务中间件
服务架构的演进:从单体到微服务的探索之旅
随着企业业务的不断拓展和复杂度的提升,对软件系统架构的要求也日益严苛。传统的架构模式在应对现代业务场景时逐渐暴露出诸多局限性,于是服务架构开启了持续演变之路。从单体架构的简易便捷,到分布式架构的模块化解耦,再到微服务架构的精细化管理,企业对技术的选择变得至关重要,尤其是 Spring Cloud 和 Dubbo 等微服务技术的对比和应用,直接影响着项目的成败。 本篇文章会从服务架构的演进开始分析,探索从单体项目到微服务项目的演变过程。然后也会对目前常见的微服务技术进行对比,找到目前市面上所常用的技术给大家进行讲解。
37 1
服务架构的演进:从单体到微服务的探索之旅
|
15天前
|
消息中间件 监控 安全
后端架构演进:从单体到微服务####
在数字化转型的浪潮中,企业应用的后端架构经历了从传统单体架构到现代微服务架构的深刻变革。本文探讨了这一演进过程的背景、驱动力、关键技术及面临的挑战,揭示了如何通过微服务化实现系统的高可用性、扩展性和敏捷开发,同时指出了转型过程中需克服的服务拆分、数据管理、通信机制等难题,为读者提供了一个全面理解后端架构演变路径的视角。 ####
38 8
|
16天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
42 5
|
18天前
|
监控 API 微服务
后端技术演进:从单体架构到微服务的转变
随着互联网应用的快速增长和用户需求的不断演化,传统单体架构已难以满足现代软件开发的需求。本文深入探讨了后端技术在面对复杂系统挑战时的演进路径,重点分析了从单体架构向微服务架构转变的过程、原因及优势。通过对比分析,揭示了微服务架构如何提高系统的可扩展性、灵活性和维护效率,同时指出了实施微服务时面临的挑战和最佳实践。
44 7
|
17天前
|
Kubernetes 负载均衡 Cloud Native
云原生架构下的微服务治理策略
随着云原生技术的不断成熟,微服务架构已成为现代应用开发的主流选择。本文探讨了在云原生环境下实施微服务治理的策略和方法,重点分析了服务发现、负载均衡、故障恢复和配置管理等关键技术点,以及如何利用Kubernetes等容器编排工具来优化微服务的部署和管理。文章旨在为开发者提供一套实用的微服务治理框架,帮助其在复杂的云环境中构建高效、可靠的分布式系统。
32 5
|
17天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####