Skip to content

Lec 11 微服务

阅读资料

博客: 微服务介绍

Introduction to microservices, 2015, blog

微服务目前备受关注:各种文章、博客、社交媒体上的讨论以及会议演讲都在讨论它们。与此同时,软件界中也有怀疑论者,他们认为微服务并非什么新鲜事物。质疑者声称这个概念只是对面向服务架构(SOA)的重新包装。然而,尽管存在这种炒作和怀疑,微服务架构模式在促进复杂企业应用的敏捷开发(agile development)交付(delivery)方面有着显著的优势

单体应用的构建

让我们想象一下,你正在开始构建一个全新的打车应用。经过一些初步会议和需求收集后,你将通过手动创建或使用像Rails、Spring Boot、Play或Maven等平台自带的生成器来创建一个新项目。

在应用的核心是业务逻辑(business logic),由定义服务、领域对象和事件的模块实现。围绕核心的是 与外部世界接口的适配器。适配器的例子包括数据库访问组件、生产和消费消息的消息组件,以及暴露API或实现UI的Web组件。

image-20250713225239427

尽管具有逻辑上模块化的架构,这个应用程序被打包和部署为单体。实际的格式取决于应用程序的语言和框架。例如,许多Java应用程序被打包为WAR文件,并部署在诸如Tomcat或Jetty之类的应用服务器上。其他Java应用程序则被打包为自包含的可执行JAR文件。类似地,Rails和Node.js应用程序被打包为目录层级结构。

采用这种风格编写的应用程序非常常见。它们开发简单,因为我们的集成开发环境(IDE)和其他工具专注于构建单一应用程序。这类应用程序的测试也很简单。你可以通过简单地启动应用程序并使用Selenium等测试包测试UI来实现端到端测试。

单体应用程序的部署也很简单。你只需将打包好的应用程序复制到服务器上。你还可以通过在负载均衡器后运行多个副本来扩展应用程序。在项目的早期阶段,这种方式运作良好。

走向单体地狱

不幸的是,这种简单的方法存在一个巨大的局限性。成功的应用程序往往会随着时间的推移而增长,并最终变得庞大。几年后,您的小而简单的应用程序将会成长为一个庞大的怪兽单体。几百万行的屎山代码。

任何试图进行敏捷开发和交付的尝试都将陷入困境。一个主要的问题是应用程序变得极其复杂。它对于任何单个开发者来说都太大了,无法完全理解。因此,修复错误和正确实现新功能变得困难且耗时。更重要的是,这往往会成为一个恶性循环。应用程序的规模也会减慢开发速度。应用程序越大,启动时间就越长

大型复杂的单体应用程序的另一个问题是,它对持续部署构成了障碍。如今,SaaS应用程序的最新技术是每天多次将更改推送到生产环境。这在一个复杂的单体应用程序中是非常困难的,因为您必须重新部署整个应用程序才能更新其中的任何一部分。我之前提到的长时间的启动时间也不会帮助。此外,由于通常无法很好地理解变更的影响,您可能需要进行广泛的手动测试。因此,持续部署几乎是不可能的。

单体应用程序在不同模块具有冲突的资源需求时,也很难进行扩展。例如,一个模块可能实现了耗费CPU的图像处理逻辑,最好部署在Amazon EC2的计算优化实例上。另一个模块可能是内存数据库,最适合部署在EC2的内存优化实例上。然而,由于这些模块被一起部署,你不得不在硬件选择上做出妥协。

单体应用程序的另一个问题是可靠性。因为所有模块都在同一个进程内运行,任何模块的bug(如内存泄漏)都有可能导致整个进程崩溃。而且,由于应用程序的所有实例都是相同的,这个bug将影响整个应用程序的可用性。

最后但并非最不重要的是,单体应用程序使得采用新框架和语言变得极为困难。例如,假设您有200万行代码是使用XYZ框架编写的。即使新的ABC框架明显更好,重写整个应用程序以使用它也将非常昂贵(无论是时间还是成本)。因此,采用新技术存在巨大的

微服务——应对复杂性

截屏2024-07-08 05.07.13

许多组织,例如亚马逊、eBay 和 Netflix,通过微服务架构模式的方式解决了这个问题。微服务架构的理念是将应用程序拆分为一组较小的、相互连接的服务。一个服务通常实现一组独特的功能或特性,例如订单管理、客户管理等。每个微服务是一个小型应用程序,具有自己的六边形架构,包括业务逻辑以及各种适配器。有些微服务会暴露一个API,供其他微服务或应用程序的客户端使用。其他微服务可能实现一个Web UI。在运行时,每个实例通常是一个云虚拟机(VM)或一个Docker容器。

现在,应用程序的每个功能区域都由它自己的微服务实现。此外,Web应用程序被拆分为一组更简单的Web应用程序,例如在我们的打车示例中,一个用于乘客,一个用于司机。这使得为特定用户、设备或专业使用场景部署独特的体验变得更加容易。

每个后端服务暴露一个REST API,大多数服务使用其他服务提供的API。然而,这些应用程序不会直接访问后端服务,而是通过API网关进行协调。API网关负责负载均衡、缓存、访问控制、API计量和监控等任务,并且可以使用NGINX有效地实现。

Scale Cube 把“如何扩展一个系统”分成了三个方向(即三个轴):

  • X-轴扩展:复制(clone)服务
  • Y-轴扩展:按功能拆分微服务
  • Z-轴扩展:按数据分片

截屏2024-07-08 05.09.53

运行时, Trip管理服务由多个服务实例组成。每个实例都是一个Docker容器,前端是一个负载均衡器(如NGINX),用于各个实例之间分配请求。

image-20250713230450836

微服务架构模式显著影响了应用程序和数据库之间的关系。每个服务都有自己的数据库模式,而不是与其他服务共享一个数据库模式。一方面,这种方法与企业级数据模型的理念相悖。此外,它经常导致部分数据重复。但是,如果您想从微服务中获益,那么为每个服务设置一个数据库模式至关重要,因为它可以确保松散耦合。下图展示了示例应用程序的数据库架构。

image-20250713230545772

每个服务都有自己的数据库。此外,服务可以使用最适合其需求的数据库类型,即所谓的多语言持久化架构。例如,驾驶员管理功能用于查找潜在乘客附近的驾驶员,必须使用支持高效地理查询的数据库。

从表面上看,微服务架构模式与 SOA 类似。两种架构都由一组服务组成。然而,理解微服务架构模式的一种方式是,它是没有商业化和 Web 服务规范 (WS-) 和企业服务总线 (ESB) 负担的 SOA。基于微服务的应用程序更倾向于使用更简单、更轻量级的协议,例如 REST,而不是 WS-。它们也非常避免使用 ESB,而是在微服务本身中实现类似 ESB 的功能。微服务架构模式也拒绝了 SOA 的其他部分,例如规范模式的概念。

微服务的好处

  • 首先,它解决了复杂性问题。通过模块化分解,通过明确每个服务的边界和RPC驱动或消息驱动的API定义,使得耽搁服务开发速度加快,也更容易理解和维护
  • 可以让一个专注于该服务的团队独立开发,只要遵守API契约
  • 每个服务能够独立部署。开发人员不需要协调仅限于他们服务的更改的部署。这种更改可以在测试完成后立即部署。例如,UI团队可以进行A/B测试,并快速迭代UI更改。使得持续部署称为可能
  • 微服务架构模式使每个服务能够独立扩展。您可以根据每个服务的容量和可用性约束,部署所需数量的服务实例。此外,你可以使用最适合服务资源需求的硬件。比如是计算密集型的或者是访存密集型。

微服务的缺点

世界上没有免费的午餐。与其他技术一样,也存在一些缺陷。

  • 一个缺点是其名称本身。微服务这个术语过分强调了服务的大小。一个缺点是其名称本身。微服务这个术语过分强调了服务的大小。但重要的是要记住,小服务只是达到目的的一种手段,而不是主要目标
  • 由于微服务是分布式系统而产生的复杂性。开发人员需要选择并实现基于消息传递或RPC的进程间通信机制。此外,他们还必须编写代码来处理部分故障,因为请求的目标可能会很慢或不可用。
  • 微服务的另一个挑战是 分区数据库架构。更新多个业务实体的业务事务相当常见。在单体应用程序中实现这些事务很简单,因为只有一个数据库。然而,在基于微服务的应用程序中,需要更新由不同服务拥有的多个数据库。使用分布式事务通常不是一个选项,不仅因为CAP理论。许多当今高度可扩展的NoSQL数据库和消息代理根本不支持它们。最终,你不得不使用基于最终一致性的方法。
  • 测试微服务应用也更加复杂。
  • 微服务架构模式的另一个主要挑战是实现跨多个服务的变更。例如,假设你正在实现一个需要对服务A、B和C进行更改的故事,其中A依赖于B,而B依赖于C。在单体应用程序中,你可以简单地更改相应的模块,集成更改,并一次性部署它们。相比之下,在微服务架构模式中,你需要仔细计划和协调每个服务的更改。例如,你需要先更新服务C,然后是服务B,最后是服务A。幸运的是,大多数更改通常只影响一个服务;需要协调的多服务更改相对罕见。
  • 部署基于微服务的应用程序也更加复杂。相比之下,微服务应用程序通常包含大量服务例如,据Adrian Cockcroft称,Hailo有160个不同的服务,Netflix有超过600个服务。每个服务将有多个运行时实例。这需要配置、部署、扩展和监控的部分更多。此外,你还需要实现一个服务发现机制,使服务能够发现其需要通信的其他服务的位置(主机和端口)。
    • 实现自动化的一种方法是使用现成的平台即服务(PaaS),如Cloud Foundry。PaaS为开发人员提供了一种简单的方式来部署和管理他们的微服务。它将开发人员从获取和配置IT资源的担忧中解脱出来。
    • 自动化微服务部署的另一种方法是开发本质上属于您自己的 PaaS。一个典型的起点是将集群解决方案(例如 Kubernetes)与 Docker 等技术结合使用。

总结

构建复杂的应用程序本身就很困难。单体架构只适用于简单、轻量级的应用程序。如果将其用于复杂的应用程序,最终会陷入困境。尽管微服务架构模式存在一些缺点和实现挑战,但它仍然是复杂且不断发展的应用程序的更佳选择。

论文:一款开源微服务性能基准套件 & 软硬件对云服务的影响

An Open-Source Benchmark Suite for Microservices and Their Hardware-Software Implications for Cloud and Edge Systems, ASPLOS‘19

摘要

本文将探讨微服务对整个云系统堆栈的影响。首先,我们介绍了 Death StarBench,这是一款基于微服务构建的新型开源基准测试套件,它代表了大型端到端服务,具有模块化和可扩展性。DeathStarBench 包含一个社交网络、一个媒体服务、一个电商网站、一个银行系统以及用于协调控制无人机集群的物联网应用。然后,我们将使用 DeathStarBench 研究微服务的架构特征、它们在网络和操作系统中的影响、它们在集群管理方面的挑战,以及它们在应用程序设计和编程框架方面的权衡。 最后,我们探讨了微服务在数百名用户的实际部署中的规模的长尾效应,并强调了它们对性能可预测性带来的更大压力。

1. 介绍

image-20250714030101323

图 1 展示了传统单体式服务和使用微服务构建的应用程序之间的部署差异。虽然整个单体式应用程序可以在多台服务器上横向扩展,但微服务允许端到端应用程序的各个组件进行弹性扩展,并将互补资源的微服务打包在同一物理服务器上。

微服务促进了可组合软件设计;支持语言和架构的异构性;简化了正确性和性能调试。尽管如此,由于与传统的云服务设计方式有显著不同,在诸多领域有广泛的影响。在本文中,我们探讨了微服务在整个云系统栈中的影响,从硬件一直到应用设计,使用一套由数十个微服务构建的新型端到端代表性应用。

每个服务包含数十个采用不同语言和编程模型的微服务。包括 node.js、Python、C/C++、Java、JavaScript、Scala 和 Go,并利用了 NGINX、mmecached、MongoDB、Cylon和Xapian等开源应用程序。为了创建端到端服务,我们使用Apache Thrift和gRPC等流行的开源框架构建自定义RPC和RESTful API。最后,为了追踪用户请求微服务中的进展情况,我们开发了一个轻量级且对用户透明的分布式跟踪系统,它可以以 RPC 粒度跟踪请求,关联属于同一端到端请求的 RPC,并将跟踪记录到集中式数据库中。我们研究了服务真实用户产生的流量,以及工作负载生成器产生的合成负载。

我们使用这些服务来研究跨系统堆栈的微服务的影响(如图2所示)首先,我们量化当前数据中心架构运行微服务的效率,以及数据中心硬件需要如何改进才能更好地适应其性能和资源需求(第 4 节)。这包括分析现代服务器的周期细分,检查大核心还是小核心更可取,确定微服务对指令缓存的压力以及探索它们在硬件加速方面的潜力。我们发现,尽管每个微服务的计算量很小,但每个层级的延迟要求比典型应用程序严格得多,这给可预测的高单线程性能带来了更大的压力。

image-20250714031019006

其次,我们量化了微服务的网络和操作系统影响。具体来说,微服务在内核中花费了大量时间。然而,与单体服务不同的是,微服务在RPC或其他REST API上发送和处理网络请求的时间要长得多。图3显示了三个单体服务(NGINX、memcached、MongoDB)和端到端社交网络应用程序的执行时间分解,其中网络处理(红色)和应用程序处理(绿色)的比例。对于单一层服务,只有很少的时间用于网络处理,但是使用微服务时,这一比例增加到了总执行时间的36.3%,导致系统资源瓶颈发生了显著变化。在第5节中,我们展示了将RPC处理卸载到与主机服务器紧密耦合的FPGA可以将网络性能提升10-60倍。

image-20250714031308921

第三,微服务显著增加了集群管理的复杂性。尽管集群管理器可以按需扩展单个微服务而不是整个单体服务,但微服务之间的依赖关系引入了背压效应(back-pressure efforts)和级联违规QoS(cascading Qos violations) ,这些问题很快传播到整个系统,导致性能变得不可预测。现有的集群管理器虽然优化了性能和/或利用率,但不足以充分考虑每对微服务依赖对端到端性能的影响。在第6节中,我们展示了即使是管理单个这样的依赖关系也会显著影响尾延迟,例如,社交网络的尾延迟可以增加10.4倍,并且系统需要较长时间才能恢复,相比之下,单体服务对应的情况要好得多。我们还展示了许多云基础设施中存在的传统自动扩展机制无法解决微服务之间依赖引起的QoS违规问题

第四,在第7节中,我们确定了微服务在端到端服务的关键路径中创建的瓶颈,并量化了在RPC和RESTful API之间的性能权衡,探索了在无服务器编程框架上运行微服务的性能和成本影响。

最后,考虑到云中的性能问题通常只在大规模下显现,在第8节中,我们利用数百用户的实际应用部署,展示了微服务相比单体应用在规模化尾部效应方面表现更加显著,因为单个配置不良的微服务或慢速服务器可以将端到端延迟降低数个数量级。

随着微服务的不断发展,数据中心硬件、操作系统和网络系统、集群管理器以及编程框架也必须随之发展,以确保它们的普及不会以性能和/或效率的损失为代价。DeathStarBench 目前已在多家学术和工业机构中使用,应用于无服务器计算、硬件加速和运行时管理。我们希望将其开源给更广泛的受众,以鼓励更多人参与这一新兴领域的研究。

2. 相关工作

代表套件包括

  • CloudSuite: 批处理和交互式服务(如memcached)
  • TailBench:聚合个交互式工作负载,Web服务器、数据库、语音识别、机器翻译
  • Sirius:只能语音助手为核心,语音转文本。

上述套件聚焦单层、或两三层服务,与现代云服务的真实部署场景不符。

随着微服务架构流行,最近研究开始关注其特征和需求:

工作内容
µSuite量化微服务中的系统调用、上下文切换、OS 开销
Ueda et al.分析计算资源、框架、容器配置对微服务性能的影响
DeathstarBench核心贡献:研究大规模多微服务系统的行为

3. DeathStarBench 套件

我们首先介绍它的设计原理,然后,介绍每个服务的架构和基础功能。

3.1 设计原则

  • 代表性:由流行的开源应用构建:如Nginx, memcached, MonogoDB, RabbitMQ,MySQL,Apache http服务器,服务之间的接口通过Apache Thrift, gPRC,或者是HTTP

  • 端到端操作:DeathStarBench从客户端生成请求开始,实现了服务的完整功能,直到请求到达服务的后端和/或返回客户端。

  • 异构性: 软件件的异构性对于微服务来说既是挑战也是机会,因为不同的语言意味着不同的瓶颈、同步原语、间接级别和开发工作量。该套件使用低级和高级、托管和非托管语言的应用程序

  • 模块化: 在端到端应用程序的设计中避免任何两个依赖微服务之间的过度双向通信,并确保它们是单一关注点且松耦合的。

  • 可重配置性:轻松更新较大服务的组件是微服务的主要优势之一。我们的RPC/HTTP API允许用小改动替换微服务的替代版本的组件。

表 1 展示了每个服务开发的 LoC,以及通信协议的 LoC;这些 LoC 是手写的,并在适用的情况下由 Thrift 自动生成。社交网络、媒体、电子商务和银行服务的大部分新代码都用于跨微服务 API,以及一些没有开源框架的微服务,例如电影评级。对于 Swarm 应用程序,我们展示了两个版本的代码细分:一个版本的大多数计算发生在后端云中(Swarm Cloud),另一个版本的计算发生在本地边缘设备(Swarm Edge)。我们还展示了每个应用程序的独立微服务数量,以及每种编程语言的细分情况。除非另有说明,所有微服务均在 Docker 容器中运行。

image-20250714032856287

3.2 社交网络

image-20250714033016509

范围:端到端服务实现了具有单向关注关系的广播式社交网络。

功能:图4显示了端到端服务的架构。用户(客户端)通过http发送请求,首先到达由nginx实现的负载均衡器。一旦选择了特定的web服务器(也在nginx中),后者使用php-fpm模块与负责组合和显示帖子的微服务进行通信,以及负责广告、搜索引擎等的微服务。在php-fpm下游的所有消息都是Apache Thrift RPCs 。用户可以创建包含文本、媒体、链接和标签的帖子发送给其他用户。他们的帖子随后被广播给所有的关注者。用户还可以阅读、收藏和转发帖子,以及公开回复或直接向其他用户发送私信。该应用程序还包括诸如广告和用户推荐引擎 的机器学习插件,使用Xapian 的搜索服务,以及用于记录和显示用户统计信息(例如关注者数量)并允许用户关注、取消关注或阻止其他账户的微服务。服务的后端使用memcached进行缓存,并使用MongoDB作为持久存储,用于帖子、个人资料、媒体和推荐。最后,该服务配备了分布式跟踪系统(第3.7节),记录每个网络请求的延迟和每个微服务的处理情况;跟踪结果存储在集中式数据库中。该服务已广泛部署在我们的机构,目前为数百名用户提供服务。我们使用这个部署来量化微服务规模化效应的尾部效应(第8节)。

3.3 媒体服务

截屏2024-07-08 06.47.21

范围:该应用程序实现了一个端到端的服务,用于浏览电影信息,以及评论、评分、租赁和流媒体电影 [18, 19]。

功能:图5显示了端到端服务的架构。与社交网络类似,客户端请求首先到达负载均衡器,该负载均衡器将请求分发给多个nginx web服务器。用户可以搜索和浏览电影信息,包括剧情、照片、视频、演员阵容和评论信息,还可以通过登录他们的账户在系统中为特定电影添加新评论。用户还可以选择租赁电影,这涉及到一个付款认证模块,用于验证用户是否有足够的资金,以及使用nginx-hls进行视频流的视频流模块,nginx-hls是用于HTTP直播流的生产nginx模块。实际的电影文件存储在NFS中,以避免访问非关系型数据库中的分块记录的延迟和复杂性,而电影评论则存储在memcached和MongoDB实例中。电影信息在分片和复制的MySQL数据库中维护。该应用程序还包括电影和广告推荐器,以及一些辅助服务用于维护和服务发现,这些服务在图中未显示。我们同样将Media Service部署为在康奈尔大学进行项目演示的托管站点,社区成员可以浏览和评论。

3.4 网络商城

截屏2024-07-08 06.48.53

范围:该服务实现了一个服装电子商务网站。设计灵感来源于并使用了开源的Sockshop应用程序的多个组件 。

功能:图6显示了端到端服务的架构。在这种情况下,应用程序的前端是一个node.js服务。客户可以使用该服务浏览库存,使用目录服务,后者是一个Go微服务,用于挖掘后端memcached和MongoDB实例中包含的关于产品的信息。用户还可以通过将商品添加到购物车(Java)来下订单(Go)。在登录到他们的账户后(Go),他们可以选择运输选项(Java),处理他们的付款(Go),并获得订单的发票(Java)。订单通过QueueMaster(Go)进行序列化和提交。最后,该服务包括一个推荐引擎用于推荐产品,并包含用于创建商品愿望清单(Java)和显示当前折扣的微服务

3.5 银行系统

截屏2024-07-08 06.49.09

范围:该服务实现了一个安全的银行系统,用户可以利用它来处理支付、申请贷款或结算信用卡账户。

功能:用户通过一个类似于电子商务服务中的node.js前端与系统交互,登录他们的账户,查询关于银行的信息,或联系客服代表。一旦登录,用户可以从他们的账户中处理支付,支付他们的信用卡账单或请求新卡,浏览有关贷款的信息或申请贷款,并获取有关财富管理选项的信息。大多数微服务使用Java和Javascript编写。后端数据库包括内存中的memcached和持久化的MongoDB实例。该服务还拥有一个关系型数据库(BankInfoDB),包括有关银行、其服务和代表的信息。

3.6 群集协调

截屏2024-07-08 06.52.02

范围:最后,我们探索了微服务的不同执行环境,其中应用程序既在云上运行,也在边缘设备上运行。该服务协调编程无人机的路由,这些无人机执行图像识别和避障。

功能:我们探索了该服务的两个版本。在第一个版本(图8a)中,大部分计算发生在无人机上,包括运动规划、图像识别和避障,云端仅为每个无人机构造初始路由(Java服务ConstructRoute),并持久保存传感器数据的副本。这种架构避免了云与边缘之间的高网络延迟,但受限于板载资源。控制器和运动控制器使用Javascript实现,而图像识别使用jimp,一个用于图像识别的node.js库,障碍物避障使用C++。无人机上的服务以本地方式运行,并通过IPC进行通信,而云端和无人机之间通过http通信,以避免在边缘设备上安装Thrift的重依赖项。

在第二个版本(图8b)中,云端负责大部分计算。它为所有无人机执行运动控制、图像识别和避障,分别使用ardrone-autonomy [2] 和Cylon [5]库,在OpenCV和Javascript中实现。边缘设备仅负责收集传感器数据并将其传输到云端,以及使用本地node.js日志记录服务记录一些诊断信息。在这种情况下,几乎每个动作都会受到云端与边缘之间的网络延迟影响,尽管服务受益于额外的云资源。我们使用了24个可编程Parrot AR2.0无人机(在图8c中可以看到部分),以及一个由20台双插槽、40核服务器组成的后端集群。无人机通过无线路由器与彼此和集群进行通信。

3.7 微服务方法论的挑战

微服务的一个主要挑战在于不能像传统的客户端-服务器应用程序那样仅依赖客户端报告性能问题。解决性能问题需要确定导致QoS违规的哪些微服务,通常通过分布式跟踪来实现。

我们开发并部署了一个分布式跟踪系统,使用Thrift定时接口记录每个微服务的RPC粒度延迟。跟踪模块在每个微服务收到和离开RPC或REST请求时对其进行时间戳记录,数据由类似于Zipkin收集器的跟踪收集器累积,并存储在集中式Cassandra数据库中。我们还追踪处理网络请求所花费的时间,与应用程序计算的时间使用类似的方法。我们验证了跟踪的额外开销是可以忽略不计的

4. 架构影响

截屏2024-07-08 16.24.31

方法论:该集群由20台双插槽、40核Intel Xeon服务器(E2699-v4和E5-2660 v3),每台服务器具有128-256GB内存,连接到一个带有10Gbe网卡的10GBps ToR交换机。所有服务器都运行Ubuntu 16.04。

周期分解和IPC:我们使用Intel vTune对周期进行分解,并识别瓶颈。上图显示了社交网络和电子商务服务中每个微服务的IPC和周期。在所有服务中,大部分周期,通常是大多数周期,都花费在处理器前端。前端停滞由于多种原因引起,包括长时间的内存访问和指令缓存未命中。这与传统云应用程序的研究结果一致

前端frond-end: 从指令缓存(i-cache)中提取指令并进行解码的部分

后端back-end: 负责指令执行、数据访问和结果写回的部分

指令退休Retiring: 如果指令退休成为瓶颈,意味着处理器的大部分时间花在了成功执行和提交指令

这里的IPC指的是的每周期指令数

电子商务包括一些与这种趋势相反的微服务,这些微服务具有较高的IPC和较高的指令退休百分比,例如搜索。搜索(xapian [51])已经针对内存局部性进行了优化,并且代码库相对较小,这解释了较少的前端停滞。相同的情况适用于简单的微服务,例如愿望清单,其指令缓存未命中几乎可以忽略不计。电子商务还包括一个推荐引擎,其IPC极低;这与对机器学习应用程序架构行为的研究结果一致[44]。

单体应用与微服务的性能比较而言,单体应用程序的周期分解和IPC与微服务相似。这表明在某些情况下,单体应用程序和微服务在性能上并没有显著差异。单体应用程序由于减少了前端停滞,经历了略高的指令提交百分比,因为它们不太可能等待网络请求完成。减少了网络通信的开销。

1级缓存缓存压力: 下图显示了社交网络和电子商务应用程序中每个微服务的MPKI(每千条指令缓存未命中数)。我们还包括了后端缓存和数据库层,以及相应的单体实现的L1i MPKI。 首先,nginx、memcached、MongoDB,特别是单体的i-cache压力仍然很大。然而,剩余微服务的i-cache压力明显较低,尤其是电子商务,这是预期的结果,因为微服务的代码占用很小。由于node.js应用程序在微服务之外的上下文中并没有低的i-cache未命中率 [88],我们得出结论,微服务的简单性导致了更好的i-cache局部性。大多数L1i未命中,特别是在社交网络中,发生在内核中,并由Thrift引起。我们还检查了LLC和D-TLB未命中,发现它们比传统云应用程序低得多,这与推动微服务主要是无状态的一致

LLC(Last Level Cache):处理器的最后一级缓存,通常是L3或L4缓存,具有较大的容量,多个处理器核心共享,用于减少主内存访问次数,提高系统性能

D-TLB(Data Translation Lookaside Buffer):用于存储数据地址映射的高速缓存,帮助加速虚拟地址到物理地址的转换过程,减少地址转换延迟。

截屏2024-07-08 16.59.07

强壮核 vs. 微弱核:关于小型服务器是否可以取代云中的高端平台,已有大量工作 。 微服务为简单核心提供了一个吸引人的目标,因为每个微服务的计算量很小。我们以两种方式评估低功耗机器。首先,我们在本地集群上使用RAPL降低所有微服务运行的频率。下图显示了随着负载增加和操作频率降低,五个流行的开源单层交互服务(nginx、memcached、MongoDB、Xapian和推荐引擎)的尾部延迟变化。我们将这些与五个端到端服务(底部行)进行比较。

RAPL(Running Average Power Limit)是英特尔处理器中的一个技术,用于监控和管理处理器的功耗。

截屏2024-07-08 17.11.56

如预期的那样,大多数交互式服务对频率缩放敏感。在单体工作负载中,MongoDB是唯一一个在最大负载下几乎可以容忍最低频率的服务,因为它是I/O绑定的。其他四个单层服务在频率下降时延迟增加,其中Xapian最敏感 [51],其次是nginx和memcached。然而,对微服务进行相同研究发现,比传统云应用程序, 端到端服务的尾部延迟更高,微服务对单线程性能差也更为敏感,单线程性能的波动或不稳定性会直接影响每个微服务的响应时间和整体服务的性能表现。社交网络和电子商务对低频率最为敏感,而Swarm服务最不敏感,主要是因为它受云-边缘通信延迟的限制,而不是计算速度的限制。

5. OS & 网络影响

我们现在探讨OS和网络在微服务模型下扮演的角色。

5.1 操作系统与用户级别分析

截屏2024-07-08 17.26.14

图中展示了每个端到端服务中内核、用户和库的周期(Cycles,C)和指令(Instructions,I)的分布情况。对于所有应用程序,特别是社交网络和媒体服务,执行的大部分时间都在内核模式下,这主要受到使用内存缓存(如memcached [57])和高网络流量的影响,同时几乎等量的时间用于像libc、libgcc、libstdc++和libpthread等库的执行。对于更加计算密集的电子商务和银行的微服务,它们在用户模式下的时间比例更加平衡,而Swarm无论是在云端还是边缘配置中,几乎有一半的时间花费在库中。内核中的大量周期并不奇怪,因为像memcached和MongoDB这样的应用程序大部分执行时间都用于处理中断、处理TCP数据包,并激活和调度空闲的交互式服务。大量的库周期也是合理的,因为微服务优化于开发速度,并且大量利用现有的库,而不是从头开始重新实现功能。

5.2 计算和通信比例

计算与通信比率:图15a展示了社交网络微服务在低负载和高负载下处理网络请求所花费的时间与应用程序计算时间的对比。图15b展示了剩余端到端服务中处理RPC请求所花费尾延迟的分数比例。在低负载下,RPC处理在社交网络的微服务中占执行时间的5-75%,占端到端尾延迟的18%。这是因为一些微服务过于简单,没有涉及到大量的处理。相比之下,在电子商务和银行中,网络处理占延迟的比例较低,主要是因为它们的微服务更加计算密集。最后,在Swarm的两种设置中,网络处理占尾延迟的比例超过30%,即使在低负载下也是如此。在高负载下,网络处理成为除电子商务和银行外所有端到端服务尾延迟的显著因素,因为NIC中的长队列不断增加。这对尾延迟产生了显著影响,社交网络端到端尾延迟增加了3.2倍。网络处理的重大影响不论微服务是通过RPC(如社交网络、媒体服务、银行)还是HTTP(如电子商务、Swarm-Edge)进行通信都存在,尽管RPC在低负载下的延迟要比HTTP低得多。最后,图15a还显示了单体社交网络应用程序处理网络请求的时间。无论是在低负载还是高负载下,差异都是显著的,尽管这是合理的,因为单体应用程序部署为单一二进制文件,大多数网络流量对应于客户端-服务器通信。

鉴于网络处理在尾延迟中的突出作用,我们现在探讨其加速的潜力。我们使用了类似于图16a中所示的“线中凸起”设置,与[38]中类似,使用Vivado HLS在Virtex 7 FPGA上卸载整个TCP堆栈[54, 69, 70, 74, 75]。FPGA位于NIC和顶部机架交换机(ToR)之间,并使用匹配的收发器与两者连接,作为网络上的过滤器。我们保持主机与FPGA之间的PCIe连接,以加速其他服务,如在低网络负载时的推荐引擎中的机器学习模型。图16b展示了通过网络处理加速带来的仅网络处理延迟和每个服务端到端延迟的加速比例。与本地TCP相比,网络处理延迟提高了10-68倍,而端到端尾延迟提高了43%至2.2倍。对于交互式、延迟关键的服务,即使尾延迟有所改善,网络加速也显著提升了性能

截屏2024-07-08 17.30.11

截屏2024-07-08 17.30.20

6. 集群管理影响

微服务会给集群管理带来复杂性,因为层级之间的依赖会带来背压效应,带来系统层面的热点问题(hotspots)。背压问题可能会误导集群管理器惩罚或者水平扩展一个饱和的微服务,即使可能是因为其依赖的服务产生了背压,而不是因为该服务本身的处理能力不足。图17突显了一个简化的两层应用程序(由Web服务器nginx和内存缓存键值存储memcached组成)。

截屏2024-07-08 17.44.58

在案例A中,当客户端发出读请求时,nginx达到饱和状态,导致其延迟迅速增加,并在其输入处形成长队列。这是一个直接的情况,自动扩展系统可以通过扩展nginx来轻松解决,如图中所示的t = 14秒和t = 35秒。案例B则突显了背压的挑战。在使用HTTP1时,单个连接内的请求是阻塞的,即跨层只能有一个未完成的请求。因此,即使memcached本身没有饱和,它也会在nginx之前形成长队列的未完成请求。这反过来导致nginx饱和。当前的集群管理器不能轻松解决这种情况,因为基于利用率的自动扩展方案会扩展正在等待且看似饱和的nginx。正如图中所示,这不仅不能解决问题,而且可能会加剧问题,因为会进一步允许更多的流量进入系统。即使在HTTP1中没有连接阻塞,背压仍然会发生,因为多层应用程序并非完全独立运作的完美管道。

级联QoS(Quality of Service,服务质量)违规指的是在一个系统中,由于某个服务的性能问题,导致其依赖的其他服务也出现性能问题,从而逐级影响到整个系统的服务质量

截屏2024-07-08 17.53.15

图19展示了社交网络服务中级联QoS违规的影响。在图19a中,较暗的颜色意味着请求时表现良好,延迟较低,接近其正常运行的预期性能,图19b中显示了低利用率。较亮的颜色表示每个微服务的高尾延迟和高CPU利用率,这表明该微服务可能在处理请求时遇到了延迟问题。微服务按照服务架构的顺序排序,从顶部的后端服务到底部的前端。图19a显示,一旦顶部的后端服务经历高尾延迟,热点会传播到其上游服务,一直到前端。在这种情况下,利用率可能会误导。尽管在图19b中,饱和的后端服务具有较高的利用率,但图中间的微服务也具有更高的利用率,而这并不意味着会导致QoS违规。相反,有些微服务具有相对较低的利用率,性能却下降,例如由于等待来自另一个饱和层的阻塞/同步请求。这突显了集群管理器需要考虑微服务之间依赖关系对端到端性能分配资源的影响的必要性。

截屏2024-07-08 18.00.46

最后,热点在不同层之间传播意味着一旦微服务经历QoS违规,它们需要比传统的单体应用程序更长的时间来恢复,即使在自动缩放机制存在的情况下,大多数云提供商都会使用这种机制。图20展示了实现微服务的社交网络和Java中单体的这种情况。在这两种情况下,QoS违规在同一时间被检测到。然而,集群管理器可以简单地实例化单体的新副本并重新平衡负载,而自动缩放则需要更长时间来提高性能,如图20b所示,自动缩放器仅仅是增加饱和服务的资源 - 如逐渐变暗的高度利用微服务。然而,具有最高利用率的服务并不一定是QoS违规的罪魁祸首[61],这使系统需要更长时间来确定性能下降背后的正确来源。因此,到问题源头被识别出来时,已经积累了相当长的队列。

7. 应用&编程框架影响

7.1 每个微服务的延迟细分

我们检查端到端服务在不同负载下是否存在不平衡。在低负载时,社交网络和媒体服务的延迟主要由前端(nginx)控制,而其余微服务几乎均匀分布。唯一的例外是MongoDB,在端到端延迟中分别占8.5%和10.3%

截屏2024-07-08 18.02.06

无服务框架:微服务通常用于无服务器编程框架。此类框架的应用程序和数据由云提供商管理,用户只需启动短期“函数”,并按请求计费。无服务器非常适合间歇性活动的应用程序,因为维护长时间运行的实例成本低廉。此外,无服务器还针对高度并行的服务,这些服务可以在短时间内受益于大量资源。同时,无服务器增加了额外的间接层,必须对应用程序进行重写才能与无服务器框架交互。此外,由于无服务器函数是短暂的,因此必须将数据存储在持久存储中,以便后续函数对其进行操作。与内存计算相比,这可能会带来显著的开销。

8. 长尾延迟的影响

我们拿 Social Network作为例子研究长尾延迟的影响

大规模级联热点:图 22a 展示了在 100 个 EC2 实例上,微服务之间依赖关系对性能的影响。图中 y 轴上的微服务按顺序排列:从后端(顶部)到前端(底部)。在初始阶段,所有微服务运行正常。但在 t = 260 秒 时,中间层(尤其是 composePostreadPost)由于一个 交换机路由配置错误 而开始过载:请求并未被正确地负载均衡到多个实例,而是集中到了某一个实例上,导致该微服务实例超载。这进而导致它们所依赖的下游服务也饱和,引发了级联效应(cascading saturation),每一层的延迟都被放大,呈现出类似图 19 中描述的“瀑布效应(waterfall pattern)”。在采样时间接近结束(t > 500 秒)时,后端服务也因为类似的原因而出现饱和,从而使得位于关键路径较前位置的微服务也被波及、发生性能退化。这一现象在 y 轴中部的微服务上表现尤为明显(图中呈现亮黄色),它们的性能早在此前的 QoS 违约中就已经开始下降。为了让系统恢复,我们引入了速率限制(rate limiting)机制,即限制用户流量的接入速率,直到当前热点服务压力减轻。尽管速率限制在抑制级联热点方面是有效的,但它也带来副作用:一部分用户请求会被丢弃,影响用户体验。

image-20250714144045669

请求偏斜:在面向用户的云服务中,负载很少是均匀分布的,部分用户往往会产生不成比例的大量请求。在实际的社交网络流量中也遵循这一原则:大约 5% 的用户贡献了超过 30% 的请求量。为了研究请求偏斜在极端情况下的影响,我们还注入了一些“合成用户”(synthetic users),这些用户生成的请求量远高于普通用户。具体来说,我们将偏斜度(skew)从 0% 变化到 99%。偏斜度的定义是 100 - u,其中 u 是生成总请求量 90% 的用户比例。图 22b 展示了不同偏斜度下,系统在满足服务质量(QoS)条件下可承载的最大负载(QPS)。当偏斜度为 0%(即请求分布均匀)时,该服务在 100 个实例的集群规模下可以实现最大 QPS。 但随着偏斜度增加,系统的有效吞吐量(goodput,即在满足 QoS 的条件下的吞吐量)急剧下降。当少于 20% 的用户产生了大部分请求时,系统的 goodput 几乎为零。

慢服务器的影响: 图 22c 展示了随着集群规模增加,少量慢服务器对整体服务质量(QoS)的影响。我们有意通过开启激进的电源管理方式,使一小部分服务器变慢(如第 4 节所述,这种方式对性能是有害的)。对于大型集群(>100 个实例),当 1% 或更多的服务器运行不良时,系统的有效吞吐量几乎为零,因为这些服务器承载了关键路径上的至少一个微服务,从而严重影响了服务质量,即使是中小型集群(例如 40 个实例),系统最多也只能容忍一个慢服务器,才能在保证 QoS 的前提下维持一定的 QPS(每秒请求数)。最后,我们将微服务架构与社交网络应用的单体架构在面对慢服务器时的表现进行了比较。在单体架构中,即使集群规模扩大,系统的有效吞吐量仍然较高,因为某个慢服务器只会影响部署在其上的单体服务实例,其它实例可以独立运行。唯一的例外是后端数据库:无论是微服务还是单体架构,数据库都被多个应用实例共享,并被分片部署在多台机器上。如果慢服务器承载了某个数据库分片,那么所有访问该分片的请求都会被拖慢。总体来说,应用的微服务依赖图越复杂,慢服务器的负面影响就越大,因为关键路径上出现性能下降的概率也随之上升。