可扩展架构:定义与实践指南

更新于 2026-01-06

SentinelOne 2022-10-27

可扩展架构:定义与实践指南

如今的软件应用比以往任何时候都更加全球化。一次幸运的推文就可能带来流量的激增。因此,构建平台的团队必须随时准备在短时间内快速扩容——这意味着在设计应用之初就要考虑可扩展架构(scalable architecture)。本文将探讨软件架构中的可扩展性:它是什么、以及在实现过程中需要关注的关键原则,例如无状态性、松耦合和异步处理。

什么是可扩展架构?

可扩展性是一种常见的跨功能需求(cross-functional requirement)。让我们先看看维基百科是如何定义它的:

可扩展性(Scalability)是指系统通过增加资源来处理不断增长的工作负载的能力。

换句话说,可扩展架构能够在不进行根本性重构的前提下,通过增加资源来应对更高的工作负载。请注意“增加资源”这一关键点:可扩展性并不意味着应用程序能在资源不变的情况下神奇地处理额外流量。

人们常常将可扩展性与云计算联系在一起。为什么?一方面,云上存在许多超大规模扩展的成功案例,比如 Netflix;另一方面,云服务提供商提供了本地基础设施难以比拟的灵活性——你无需提前规划硬件,只需几分钟就能启动新的计算资源。通过 Terraform 等工具以代码方式(Infrastructure as Code)配置这些资源,还能带来诸多好处:快速迭代、灵活实验,并找到最适合自身需求的解决方案。我认为,对大多数组织而言,依赖云平台已成为默认选择。

为什么不采用垂直扩展?

通过购买更强大的硬件来“砸钱解决问题”在某些场景下是有效的,这被称为垂直扩展(vertical scalability)——即为现有资源增加更多计算能力。虽然这种方法简单直接,但缺点也很明显:成本可能极高,甚至可能遇到硬件上限——市场上根本没有更大规格的服务器可用。

因此,水平扩展(horizontal scalability)——即增加更多相同规格的硬件实例——已成为主流做法。接下来介绍的技术原则正是基于这一理念。

可扩展架构的核心原则

如果你翻开一本现代架构书籍(比如《软件架构基础》Fundamentals of Software Architecture),很快会意识到一件事:并不存在一种放之四海而皆准的架构模型。架构是随着时间有机演化的。因此,提供一份“万能蓝图”既不现实,也脱离实际。不过,仍有一些通用原则适用于大多数场景。下面我们就来探讨这些原则。

1. 无状态性(Statelessness)

无状态的系统更容易扩展。想象一下,你有一个 Web 服务器处理请求。如果你遵循 RESTful 原则,这些服务器就是无状态的——这意味着你可以通过增加实例轻松实现水平扩展,并在前端放置一个负载均衡器(load balancer)将流量分发到各个实例。这是一种经过验证且高效的策略,足以支撑大多数应用场景。

再举个例子:容器技术(如 Docker 和 Kubernetes)如今无处不在。Kubernetes 尤其擅长根据自动触发条件快速启动大量容器——特别是当这些容器无需保存状态时。

当然,完全无状态的应用其实并不实用。试想一个社交平台,每次刷新浏览器都会丢失所有消息,这显然不可接受!然而,尽可能将状态集中管理是明智的做法。在多层架构(multitier architecture)中,通常将应用划分为不同层级(如表现层、后端逻辑层和数据层),并将状态尽可能限制在数据层。这样做虽不能彻底消除状态问题,但能显著降低其影响范围。

2. 松耦合(Loose Coupling)

松耦合意味着系统的各个子模块之间没有强依赖关系,因此可以独立修改某个组件,而无需大幅改动其依赖项。这是软件工程中的常见模式。

那么,这与可扩展性有何关联?它能大大简化扩展问题!如果每次扩展都需要重写应用的大部分代码,成本将高得令人望而却步,而且效率低下。此外,并非系统中的每个组件都有相同的扩展需求。有针对性、细粒度的变更更容易成功,也更便于分配给不同团队并行开发——毕竟,单个团队的能力终究有限。

这就引出了微服务(microservices)的话题。需要明确的是:你并不一定需要微服务才能实现可扩展性。但如果合理使用,微服务架构能提供一种在单体应用(monolith)中难以复制的松耦合特性。当然,这也带来了挑战:分布式系统的复杂性不容小觑。我个人认为,领域驱动设计(Domain-Driven Design, DDD)对于应对这种复杂性非常有帮助。

3. 异步处理(Asynchronous Processing)

即使你已将系统拆分为多个小型模块,它们之间仍需通信。而通信就会引入依赖——而依赖往往是性能瓶颈的根源。

假设你有一组高性能 Web 服务器,需要触发某个耗时较长的处理任务。如果采用同步通信,Web 服务器就必须等待该任务完成,从而自身也变得缓慢。解决方法是采用异步通信:用事件(event)代替直接调用:

  • 生产者(Producer)将事件发送到消息队列;
  • 消费者(Consumer)按自己的节奏处理这些事件。

这样一来,生产者无需等待,且与消费者保持松耦合。这种模式被称为事件驱动架构(event-driven architecture)。

既然事件机制如此优秀,为何不从一开始就全面采用异步通信?原因在于:异步逻辑更难理解和测试。你又看到了那个熟悉的模式——架构设计总是充满权衡,天下没有免费的午餐。

4. 利用托管基础设施(Leverage Managed Infrastructure)

一种常被低估的扩展方式,就是让别人替你干活
你决定用 Kubernetes 运行应用?很好。但与其自己运维集群,不如选择托管服务(如 AWS EKS)。亚马逊在大规模运行软件方面经验丰富,使用其托管服务能让你更专注于核心业务能力。

在数据存储方面,托管基础设施的优势尤为突出。无论是分布式 SQL 数据库、NoSQL,还是对象存储,云服务商都已提供成熟方案。运维可扩展的数据存储是一项充满陷阱的艰巨任务,除非你的组织技术非常成熟,否则我强烈建议将这项工作外包出去——对大多数团队而言,这更具成本效益。

同样的思路也适用于工具和框架。开源社区已有大量经过实战检验的软件(如 Kafka),何必从头构建内部事件流平台?

走向更可扩展的架构:避免反模式

在迈向可扩展架构的过程中,以下几点需要特别警惕:

  • 过早预测远期需求:为几年后“可能”出现的需求提前构建功能,往往导致大量无谓投入。准确预判未来需求极其困难。
  • 追求“大爆炸式”发布(Big Bang release):应采用渐进式变更,每次只改一处。小步快跑能最大限度降低风险。
  • 凭直觉做决策:用数据验证假设。若缺乏可靠数据,就通过原型(prototype)来获取。
  • 不敢尝试新方案:过度保守会阻碍创新。勇于实验,才能发现真正有效的路径。

可观察性在可扩展系统中的作用

可观察性(Observability)是另一项关键的跨功能需求。简而言之,它衡量的是你能否通过外部观测理解系统的当前状态。

这与可扩展性密切相关:要有效扩展系统,必须深入理解其行为——而可靠的数据是基础。日志(logs)、指标(metrics)和追踪(traces)共同为你提供决策所需的信息。

当然,构建完整的可观察性工具链需要额外投入。与其自研,不如采用成熟方案(如 Scalyr),这通常是更明智的投资。

架构需要持续演进

正如开头所说,软件架构不是一成不变的。即使你的可扩展性需求暂时稳定,架构本身也永远不会“完成”。我最诚恳的建议是:培养演进式架构思维(evolutionary architecture),并能快速响应新发现——这才是敏捷之道。

迈向可扩展系统

需要扩展,其实是个“幸福的烦恼”——说明你的应用正被广泛使用。通往可扩展性的道路并非唯一。我建议你:
✅ 将架构拆解为更小、松耦合的组件;
✅ 在必要时采用异步通信;
✅ 充分利用云技术快速迭代,构建能应对各种场景的解决方案。

可扩展性不是终点,而是一段持续优化的旅程。