2.2 水平扩展
水平扩展是指依靠架构复制服务,并在多个服务器节点上运行多个服务副本的能力。来自客户端的请求被分布在不同的副本,理论上讲,如果有N个服务副本和R个请求,则每个服务器节点处理R/N个请求。这种简单的策略增加了应用程序的存储能力和计算能力,从而提升了可扩展性。
要成功扩展应用程序,你的设计中需要具备两个基本元素。如图2-2所示,它们分别是:
负载均衡器
所有用户请求都发送到负载均衡器,由它来决定处理请求的目标服务副本。关于如何选择目标服务副本,有多种策略,所有策略的核心目的都是让不同的资源同等繁忙。负载均衡器还负责将来自服务副本的响应转发回客户端。大多数负载均衡器都属于反向代理的互联网组件(https://oreil.ly/78lLN),它们控制着客户端请求对服务器资源的访问。负载均衡器扮演着中介的角色,反向代理为请求添加了额外的网络跳跃点,它们需要将延迟控制到极低,才能最大限度地减少引入的开销。有许多现成的负载均衡解决方案以及特定于云提供商的解决方案,我将在第5章中更详细地介绍它们的特征。
无状态服务
为了使负载平衡有效并均匀地分发请求,负载均衡器必须可以自由地将来自同一客户端的连续请求发送到不同的服务实例进行处理。这意味着服务中的API实现不得保留与单个客户端会话相关联的任何知识或状态。当用户访问应用程序时,服务会创建一个用户会话,并在内部管理一个唯一的会话,以识别与该用户交互的顺序并跟踪会话状态。一个典型的会话状态管理例子是购物车。为了有效地使用负载均衡器,代表用户购物车当前内容的数据必须存储在某个地方——通常是数据存储器——任何服务副本接收到请求(作为用户会话的一部分)时都可以访问此状态。如图2-2所示,上述过程的数据存储被标记为“会话存储”。
图2-2:水平扩展架构
水平扩展很有意思的地方是,理论上,你可以不断添加新的(虚拟)硬件和服务来处理增加的请求负载,并持续保持低延迟。一旦你看到延迟上升,就部署另一个服务器实例。因为使用的是无状态服务,扩展过程中无须更改代码,你只需要为部署的硬件付费,所以这是一种相对便宜的策略。
水平扩展还有另一个极具吸引力的功能:如果其中一项服务副本宕机,那么它正在处理的请求将丢失,而宕机的服务副本不管理会话状态,请求可以简单地由客户端重新发出,发送到其他服务实例进行处理。应用程序对服务软件和硬件中的故障具有弹性恢复能力,从而提高了应用程序的可用性。
然而,与任何工程解决方案一样,简单的水平扩展也有局限性。随着添加服务实例越来越多,请求处理能力会增长,理论上会无限增长。到了一定阶段,现实会让人清醒,因为单一数据库提供低延迟查询响应的能力将会减弱。缓慢的数据库查询意味着客户端的响应时间变长。如果请求的到达速度比处理速度快,一些系统组件会因为资源耗尽而过载和失败,客户端便会收到异常和请求超时。从本质上讲,数据库成了性能瓶颈,必须对其进行设计才能进一步扩展应用程序。