21CTO导读:单体架构有它的优势,微服务有新的优点,各有所长,我们来看它们在技术和参数上的比较。相信对你有所帮助。
微服务架构是为了"对抗"一些所谓的单体架构而提出的,它将业务流程拆分为多个独立的服务。
例如在机票预订的场景下,单一方法涉及构建具有“预订机票”流程的单一软件。
“预订机票”涉及多个单独的流程。可能要向航空公司预订机票、向客户信用卡收费,并在机票预订成功后向客户发送确认信息。
在微服务架构中,各个流程被分解为独立的服务。在上面的业务例子中,服务可以是机票预订、信用卡支付和确认。现在,独立的服务通过定义的接口相互通信。
如果说微服务架构已成为主流,这还只是轻描淡写。在我们的文章列表中,我几乎再也看不到单体架构了——只有在关于从微服务回归单片架构的文章中才会看到。在本文中,我们将对这两者进行对比。
两种软件架构风格进入竞争,其中一种将会胜出
第一轮:延迟
谈到微服务时,有一条基本物理定律在起作用。每当一个微服务通过网络调用另一个服务时,字节就会通过网络发送。这涉及将字节转换为电信号或光脉冲,然后将这些信号转换回字节。根据此链接,微服务调用的延迟至少为 24 毫秒。如果我们假设实际处理大约需要 100 毫秒,那么总处理时间将如下所示:
网络延迟——微服务与整体式架构
理想情况下,所有调用执行可以同时发生,并且不相互依赖。下面的图表显示了随着越来越多的调用同时执行时,总时间如何减少。
同时执行更多调用意味着总执行时间减少
并行执行所有调用,意味着服务将在最长的调用完成后返回给消费者。整体式架构没有网络延迟,因为所有调用都是本地的。即使在完全可并行的世界中,整体式架构仍然会更快。解决这个问题的方法是减少调用链长度,使用扇出并尽可能保持数据本地化。此外,使用扇出模式可以显著提高性能,如上所示。但最终,微服务在延迟方面无法超越物理层。
这对于单体式架构来说是一个显式的胜利。
第二轮:复杂性
考虑复杂性时,有许多因素在起作用:开发的复杂性和运行软件的复杂性。
对于开发的复杂性,在构建基于微服务的软件时,代码库的大小会快速增长。涉及多个源代码,使用不同的框架甚至不同的语言。
由于微服务需要彼此独立,因此经常会出现代码重复。此外,由于发布计划不同步,不同的服务可能使用不同版本的库。对于运行和监控方面,受影响的服务数量非常重要。单体式架构只会与自己对话。这意味着它在处理流程中有一个潜在的合作伙伴。微服务架构中的单个调用可以访问多个服务。这些服务可以位于不同的服务器上,甚至可以位于不同的地理位置。
在单体应用中,日志记录就像查看单个日志文件一样简单。但是,对于微服务,跟踪问题可能涉及检查多个日志文件。不仅需要找到所有相关的日志输出,还需要按正确的顺序将它们放在一起。微服务对每个调用使用唯一的 ID 或跨度。这允许 Elasticsearch 等工具查找跨服务的所有相关日志输出。Jaeger 等工具可以跟踪和分析跨多个微服务的调用。
在Kubernetes 集群中运行微服务时,复杂性进一步增加。虽然 Kubernetes 支持自动扩展等功能,但它并不是一个易于管理的系统。
要部署一个单体式架构,一个简单的复制操作就足够了。如果要启动或停止单体式架构,通常一个简单的命令就足够了。另一方面,Kubernetes 并不适合胆小的人。与单体式架构相比,事务也增加了运行微服务架构的复杂性。跨越服务边界,很难保证数据同步。例如,实施不当的调用重试可能会导致支付两次。微服务架构可以使用中央协调器等技术来管理这个问题。在单体式架构中,事务很容易处理,甚至对开发人员是透明的。
就复杂性而言,单体式架构又获得了胜利。
第三轮:可靠性
由于单体中的所有调用都是本地的,因此不存在网络故障的可能性。与微服务相比,假设一个微服务通过网络调用另一个服务,可靠性为 99.9%。这意味着每 1000 次调用中,一次会因为网络问题而失败。现在,如果该服务调用另一个服务,我们的可靠性为 99.8%。对于深度为 10 次调用的调用链,可靠性降至 99% — 这意味着每 100 次调用中就有 1 次会失败。
随着呼叫链变长,可靠性会下降
在设计微服务架构时,重要的是要假设网络会在某个时候中断。微服务提供了一些解决方案来解决这个问题。
开源的Spring Cloud为 Java 提供了透明的负载平衡和故障处理。Istio 等服务网格可以为多种语言实现这一点。网络问题在微服务架构中更常见,但由于这是预料之中的,因此它们被设计为处理这些问题。最重要的是,由于网络故障经常发生,微服务已经证明了它们有能力处理这些问题。这确保了这些问题没有隐藏的错误。
当微服务集群中的服务发生故障时,集群管理器将直接启动替代服务。这使得微服务架构具有高度的弹性。Netflix 创建了一个名为Chaos Monkey的工具,可以随机终止虚拟机和容器。这样他们就可以确保系统能够处理生产环境中的中断。单体当然也可以在大规模集群中运行。但由于其规模,问题对它们的打击更大。很难想象随机重启单体以确保它们能够生存下来。另一方面,最可靠的软件是单体,例如工业控制器和飞机飞行控制系统。构建高度可靠的单体绝对是可能的,但在规模和云端时会变得困难。
在此项,微服务获得了胜利。
第四轮:资源使用情况
如果微服务调用使用相同的算法执行相同的工作,它将始终比单体式调用使用更多的资源。Docker 和虚拟机会增加开销。另一项基准测试发现,在 Docker 容器中运行时,连接数下降了约 8%。图像编排也会消耗资源,日志聚合和监控也是如此。
但是,微服务会让我们能够更智能地使用资源。由于集群管理器可以根据需要分配资源,因此实际资源使用量可以低得多。看看一个在 20% 的代码中完成 80% 工作的单体应用,我们可以展示如果可以独立扩展代码的“热门”部分会发生什么。
例如,如果单体应用的一个实例使用 8GB,两个实例使用 16GB,依此类推。让我们假设 20% 可以并行执行以完成繁重的工作。我们有一个 8GB 的实例,然后微服务使用 20% 的内存,即 1.6GB。这意味着对于两个实例,我们的内存使用量为 9.6GB。
下图显示了资源使用情况的差异。
单体式架构比微服务需要更多资源,因为运行实例更多
在极端情况下,单体式架构的表现要优于微服务。例如,如果一个调用传输了大量数据情况下。
但在大多数场景下,资源使用率较低,这对微服务来说是个胜利。
第五轮:可扩展性
有多种方法可以扩展单体式架构。
可以运行多个实例并相应地做路由请求。或者可以运行多个线程或使用非阻塞 IO。对于微服务架构,这三种方法也都适用。正如在资源使用情况中所述,它们可以用更少的资源完成。这意味着每个资源可以处理更多的连接。对于花在资源上的钱,微服务提供了更高的吞吐量。此外,可以进行更精确的扩展。如果整体式架构正在使用所有资源,处理更多连接的方法是启动第二个实例。
如果单个微服务使用了所有资源,则只有此服务需要更多实例。由于微服务的资源密集程度较低,因此可以节省资源。由于扩展简单且精确,这意味着只使用必要量的资源。管理员可以根据需要将 AWS 或其他云提供商联机或脱机。
例如,假设一个单体应用正在目前最大的 Amazon Web Services 实例上运行。m5.24xlarge实例提供高达 96 个 CPU 和 384 GB RAM。目前,该实例的成本也高达每月 2,119.19 美元,并且全天候运行。同样的价格可以购买 12 个c5.2xlarge实例,每个实例具有 8 个虚拟 CPU 和 16GB RAM,全天候运行。但大多数工作负载不需要全天候使用全部资源。相反,资源使用率在某些时段达到峰值,其余时间较低。下面的图表显示了如果这 12 个较小的实例只运行一段时间而不是全天候运行,它们的成本是多少。
由于专用资源比按小时收费的资源便宜,因此单个实例的价格会有所下降。本例中的交叉点是 520 小时,或者大致相当于实例运行时间占 70%。
比较 12 个按需微服务容器与专用大规模单一容器的价格
由于微服务架构更加细粒度,因此单个服务的扩展也更加细粒度。
对于精确的扩展和更好的资源利用来说,这对微服务来说是一个显式的胜利。
第六轮:吞吐量
让我们再看一个性能指标:绝对吞吐量。
我们已经探讨了网络延迟和并行化之间的关系。吞吐量也存在同样的关系。在无法跨网络并发运行的工作负载中,单体式架构可能提供更好的性能。数据需要在服务之间发送,而且所有基础设施都会产生一定的开销。如果工作负载无法扩展到多个实例,单体式架构可以提供更高的吞吐量。
由于工作负载高度本地化,且没有容器、容器编排或服务网格带来的开销,这是单体架构的优势之所在。
第七轮:上线时间
人们选择微服务架构的原因之一是上线时间——这是从对某个功能做出业务决策到该功能公开可用的时间。由于其规模和依赖性,单体式架构通常更难部署。
另一方面,经常或持续部署微服务则更容易一些。原因之一是更改的影响是高度本地化的。微服务应该只公开其接口而不是其实现。这意味着开发人员可以在不修改依赖服务的情况下更改实现。由于接口也很清晰并且可以进行版本控制,因此对它的任何更改都应该具有明确定义的影响。
而对于单体式架构,面向对象编程允许分离接口和实现。但它需要一支高度自律的团队,以免成为依赖实现的诱惑的牺牲品。
此外,微服务更易于测试。由于微服务仅涵盖有限的功能集,因此依赖项的数量也较少。依赖项越少,测试编写和运行的速度就越快。
第三,微服务的资源密集程度越低,并且可扩展。这使得微服务可以无缝推出。微服务可以在部分集群节点上启动。然后,用户可以依次迁移到新版本。其他迁移策略包括同时运行新旧版本。如果出现问题,这可以快速回滚到旧版本。微服务的细粒度架构允许更快、更强大的推出。这缩短了从构思到生产部署的时间。
此轮,微服务又赢得了胜利。
第八轮:沟通与成本
微服务通常以开发团队的规模来定义。
例如,亚马逊有一个“两个披萨规则”,即一个开发团队可以在创建一个微服务所需的时间内“吃两个披萨”,而这个微服务相当于大约 4-8 人的团队规模。其背后的想法是,沟通仅限于团队内部。团队只需通过服务接口进行沟通。
早在微服务理念诞生之前,Fred Brooks 撰写了一本影响深远的著作《人月神话》。这本书的要点之一是,沟通渠道的数量会随着团队成员数量的增加而增加。如果团队只有两个人,则只有一个沟通渠道。如果团队有四个人,则最多有六个渠道。第一个人与第二个人、第三个人和第四个人交谈。第二个人与第三个人和第四个人交谈,第三个人与第四个人交谈。沟通渠道数量的公式为 n(n − 1) / 2。
那么,一个拥有 20 名开发人员的团队有 190 个可能的沟通渠道。
将这些开发人员分成两个披萨团队,将会大大减少沟通渠道的数量。如果我们以 20 名开发人员为例,将其分成四个微服务团队,每个团队有 5 人,那么每个团队就有 10 个沟通渠道。而四个团队之间的沟通渠道只有六个。沟通渠道总数为 46 个,大约是 20 人团队的四分之一。
下图显示了一个大团队与单个微服务团队的沟通渠道数量。
随着团队规模的增长,单体式架构与微服务架构的沟通渠道
当团队规模达到 10 名开发人员时,微服务模型就比传统模型具有明显优势。
当团队规模达到 50 名开发人员时,沟通渠道的数量几乎是传统模型的 10 倍。在这种规模的团队中工作过的每个人都可以证明,沟通需要花费大量时间。单单是 50 名开发人员参加的一次站立会议就显得效率低下。任何超过 10 名开发人员的开发项目,最好将其拆分成更小的团队。微服务非常适合这种模式,因为它们在服务边界上具有定义明确的接口。
微服务的又一次的明显胜利。
谁将是赢家?
结果已经出来了:单体式架构获胜两次,而微服务架构获胜三次。
但是,在我们查看上面的图表时,请记住它是相对的。
例如,微服务仅在团队规模超过 10 名开发人员时才能赢得团队沟通。拥有 5 名开发人员的小型创业团队更适合使用单体架构。
单体架构更易于管理,移动组件更少。而专注的小型团队也可以使用单体架构运行每日发布计划。扩展仅在一定用户或使用量下才有意义。
如果产品每秒获得几次点击,单体架构可能完全足够。此外如果调用通过网络移动大量数据,性能损失可能很大,并超过其他优势。
微服务并不是解决所有开发问题的万能药。以下是一些我们总结的迹象,它们表明微服务架构可能是一个不错的选择:
需要 24/7 全天候可靠性
扩展到超过几个请求
峰值和正常负载明显不同
超过 10 名开发人员
业务领域可以切分为更小的领域
较短的操作
操作可以表达为 REST 调用或队列事件。
没有严格的跨服务交易要求
如果符合以上的标准,微服务将是企业软件或项目的强大选项。