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

本文涉及的产品
服务治理 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

原文发布时间:2020-05-12
本文作者:王欢
本文来自:“dockone”,了解相关信息可以关注“dockone”

相关文章
|
1天前
|
监控 持续交付 Docker
使用Docker进行微服务架构的最佳实践
【5月更文挑战第10天】本文探讨了使用Docker实施微服务架构的最佳实践。首先,理解微服务架构是拆分小型独立服务的模式,借助Docker实现快速部署、高可移植性和环境一致性。Docker的优势在于服务扩展、容器编排、自动化构建与部署。最佳实践包括:定义清晰服务边界,使用Dockerfile和Docker Compose自动化构建,利用Docker Swarm或Kubernetes编排,实施服务发现和负载均衡,监控与日志记录,以及持续集成和持续部署。Docker虽重要,但需与其他技术结合以确保系统整体稳定性。
|
1天前
|
缓存 负载均衡 API
微服务架构下的API网关性能优化实践
【5月更文挑战第10天】在微服务架构中,API网关作为前端和后端服务之间的关键枢纽,其性能直接影响到整个系统的响应速度和稳定性。本文将探讨在高并发场景下,如何通过缓存策略、负载均衡、异步处理等技术手段对API网关进行性能优化,以确保用户体验和服务的可靠性。
|
1天前
|
监控 持续交付 开发者
构建高效微服务架构:后端开发的新范式
【5月更文挑战第10天】在现代软件开发领域,微服务架构已经成为一种流行的设计模式,它通过将大型应用程序拆分为一组小型、独立和松散耦合的服务来提供更高的可伸缩性和灵活性。本文深入探讨了微服务架构的设计理念、实施步骤以及面临的挑战,并提出了一套实用的策略和最佳实践,帮助后端开发者构建和维护高效的微服务系统。
|
2天前
|
存储 监控 API
构建高效微服务架构:后端开发的现代实践
【5月更文挑战第9天】 在本文中,我们将深入探讨如何在后端开发中构建一个高效的微服务架构。通过分析不同的设计模式和最佳实践,我们将展示如何提升系统的可扩展性、弹性和维护性。我们还将讨论微服务架构在处理复杂业务逻辑和高并发场景下的优势。最后,我们将分享一些实用的工具和技术,以帮助开发者实现这一目标。
|
2天前
|
负载均衡 算法 NoSQL
探索微服务架构下的服务发现与治理
【5月更文挑战第9天】 在当今的软件开发领域,微服务架构已成为构建可伸缩、灵活且容错的系统的首选模式。随着服务的增多,如何有效地进行服务发现与治理成为了关键的挑战。本文将深入探讨微服务环境中服务发现的机制和治理策略,分析不同服务发现工具的优缺点,并提出一种基于一致性哈希和健康检查相结合的服务治理方案,旨在提高系统的可用性和性能。
|
3天前
|
监控 API 持续交付
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第8天】在当今快速演进的软件开发领域,微服务架构已经成为实现敏捷开发、持续交付和系统弹性的关键模式。本文将探讨构建一个高效且可靠的微服务系统所必须的策略和最佳实践。我们将从服务的划分与设计原则出发,讨论如何通过容器化、服务发现、API网关以及断路器模式来优化系统的可伸缩性和鲁棒性。此外,我们还将涉及监控、日志管理以及CI/CD流程在确保微服务架构稳定运行中的作用。
|
3天前
|
消息中间件 Java 微服务
Java微服务架构实践指南
Java微服务架构实践指南
12 0
|
3天前
|
Kubernetes 持续交付 开发者
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第8天】 随着现代软件开发的不断演进,微服务架构已成为众多企业解决复杂系统问题的首选方案。本文深入探讨了微服务架构的核心概念、设计原则以及实施策略,旨在为后端开发者提供一种清晰、高效的技术路径。通过分析微服务的优势与挑战,结合具体的应用实例,文章将展示如何通过容器化、服务网格和持续集成/持续部署(CI/CD)等先进技术手段,实现后端服务的高可用性、可扩展性和敏捷性。
|
3天前
|
消息中间件 监控 Java
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第8天】随着现代软件开发的复杂性日益增加,传统的单体应用架构逐渐难以满足快速迭代和灵活部署的需求。微服务架构作为一种新的解决方案,以其模块化、独立性强和易于扩展的特点,正在成为后端开发领域的重要趋势。本文将深入探讨如何构建一个高效的微服务架构,并分析其对后端开发实践的影响。
|
3天前
|
敏捷开发 持续交付 API
构建高效微服务架构:后端开发的现代实践
【5月更文挑战第8天】 在数字化转型的浪潮中,微服务架构已成为企业追求敏捷开发、持续交付和系统弹性的关键解决方案。本文将深入探讨微服务的核心概念,包括其设计原则、优缺点以及如何在后端开发中实现高效的微服务架构。我们将通过实际案例分析,展示微服务如何帮助企业快速适应市场变化,同时保持系统的可维护性和扩展性。