1.5 可扩展性与成本

让我们举一个简单的假设例子来观察可扩展性和成本之间的关系。假设我们有一个基于Web的系统(例如Web服务器和数据库),它可以处理100个并发请求,平均响应时间为1 s。我们有一个业务需求要求扩展这个系统,即在相同的响应时间下处理1000个并发请求。在不做任何改动的情况下,对系统进行简单的负载测试,性能如图1-2(左)所示。随着请求量的增加,我们看到平均响应时间稳步增长,在预期请求量下增长到10 s。显然,这个部署配置不能满足我们当前的要求,系统无法扩展。

图1-2:扩展应用程序;左侧表示不可扩展的性能,右侧表示可扩展的性能

为达到所需的性能,需要在工程方面做一些努力。图1-2(右)显示了修改后的系统性能。它现在能在特定的响应时间内处理1000个并发请求。因此,我们成功地扩展了系统。放松时间到了!

然而,一个重要问题迫在眉睫。需要多少努力和资源才能实现这一性能?也许只是在更强大的(虚拟)机器上运行Web服务器。在云上执行这类操作,即重新配置系统最多需要30 min。稍微复杂一点的是重新配置系统来运行多个Web服务器实例,以增加容量。上述两种方法只是对应用程序配置进行简单、低成本的更改,无须更改代码。这是极好的结果。

事实上,扩展系统并非如此简单。原因是多种多样的,可能是:

1.每秒1000个请求时,数据库的响应速度变慢,数据库服务器需要升级到新机器。

2.Web服务器动态生成大量内容,增加了相应请求量下的响应时间。一种可能的解决方案是更改代码,更有效地生成内容,减少每个请求的处理时间。

3.当许多请求尝试同时访问和更新相同的记录时,会在数据库中创建性能热点。这需要重新设计数据库schema并重新加载数据库,以及更改数据访问层的代码。

4.选择的Web服务器框架强调的是易于开发而不是易于扩展。它的执行模型意味着在该框架内根本无法通过扩展代码来满足高请求量的要求,需要使用其他框架甚至另一种编程语言来完全重写代码。

虽然还有无数潜在的其他原因,但我希望上面这些原因能说明当我们从选项1向选项4移动时,可能需要越来越多的努力。

现在让我们假设选项1,升级数据库服务器,需要15 h的工程努力和每月1000美元的额外云成本来获得更强大的服务器。这还不算太昂贵。让我们假设选项4,重写Web应用程序层,由于要用新的编程语言重构(例如,从Ruby向Java迁移),还需要10 000 h的开发时间。选项2和选项3的成本介于选项1和选项4之间。10 000 h的开发成本非常可观。更糟糕的是,在开发过程中,由于无法满足客户的访问需求,该应用程序可能会失去市场份额并因此失去资金。这些情况可能会导致系统和业务失败。

这个简单的场景说明了资源和成本如何与系统可扩展性密不可分。如果系统一开始的设计没有考虑可扩展性,那么为了满足新的访问需求,增加系统容量所需的下游成本和资源可能是巨大的。对于某些应用程序,需要承担修改业务系统来满足最终业务需求的成本,例如HealthCare.gov(https://oreil.ly/P7nyc),付出了超过20亿美元的代价。对于其他系统,无法以低成本快速扩展,高昂代价可能就是它的“丧钟”,例如俄勒冈州的医疗保健交易所(https://oreil.ly/fTDcc),扩展的成本为3.03亿美元。

我们永远不可能看到有人会尝试将郊区住宅的容量扩大成50层的办公楼。房子建筑的结构、材料和地基无法支撑这样的需求,如果不将它完全拆除和重建,这是一个遥不可及的目标。同样,我们不应该期望一个没有采用可扩展架构、机制和技术的软件系统快速发展以满足更大的容量需求。规模化的基础需要从一开始就建立好,而且要认识到组件将随着时间的推移而发展。采用促进可扩展性的设计和开发原则,我们可以更快速、更低成本地扩展系统以满足快速增长的需求。我将在本书的第二部分解释这些原则。

在成本线性增长的同时支持指数级扩展的软件系统被称为超大规模系统,我将其定义如下:“超大可扩展系统在计算和存储能力方面表现为指数级增长,同时在所需资源成本方面表现为线性增长,成本包括在构建、运维、支持和发展系统过程中所需的软件和硬件资源。”你可以在https://oreil.ly/WwHqX这篇文章中阅读有关超大规模系统的更多信息。