在进入这个进阶版系列之前,让我们先回顾一下云计算与大数据系统的基本设计原则,总结起来有如下几条:
(1)基础架构:更多采用商品现货硬件(如PC架构),而很少使用定制化高端(如小型主机)硬件。
(2)扩展性:追求动态扩展、横向扩展,而非静态扩展、垂直扩展。需要指出的是,系统扩展性不能是以牺牲性能为前提的,如我们在前面的文章中讨论过的,横向扩展的系统在简单、浅层查询的高并发场景中有优势,但是在复杂、深层查询的场景中,垂直扩展的系统更具优势,因此,系统扩展过程中通常是纵向扩展与横向扩展兼而有之的。
(3)可用性:追求弹性而非零故障(取决于商品现货硬件)。
(4)高性能:通过分解与分布而非压迫来实现。
(5)可用性:更多地关注平均故障恢复时间而非平均故障间隔时间,这一点是使用了商品现货硬件后做出的改变。
(6)容量规划:相对于第二平台架构设计中的最差情形规划(即导致大量资源被浪费的最大负载规划),第三平台(云计算与大数据)的规划精细度要高得多,多采用细颗粒度。
(7)期望失败:可以容忍部分系统组件的失败,以此来换取整个系统的持续在线。微服务架构与面向灾难(失败或死机)的系统架构、服务与测试设计框架可以说都是在期望失败思路的指导下诞生的。
基于这些基本设计原则,我们来逐一分析可扩展系统构建、开源开发与商业模式,以及从服务驱动的体系架构到微服务架构这3个方面。
构建可扩展系统的目的是实现可扩展的应用与服务。我们首先了解一下可扩展应用与服务的九大误区:
①完全依赖本地资源:数据没有实现云(网络)存储。
②服务采用强依赖性:服务的强依赖性指的是B服务依赖于A服务,而A服务下线后将直接导致B服务的下线。在第三平台的架构设计中,我们应把B服务设计为当A服务不可用时,采用其他渠道继续提供服务,例如从内容分发服务(Content DeliveryNetwork,CDN)或缓存区中保存的数据继续提供服务,以此来提高用户体验,同时在后台通知DevOps团队修复故障服务。
③CDN无用论:CDN的最大价值在于降低服务器和网络的压力,以及提升用户体验。除非自建CDN,否则利用现有CDN服务提供商网络不失为上策(规模经济效应)。
④缓存无用论:缓存是提供系统整体效能加速的一把双刃剑,好的设计原则会让系统事半功倍,坏的设计原则可能让用户总是得到过期的数据。在后文中我们会介绍正确使用缓存的方法。
⑤纵向扩展而非横向扩展:纵向扩展的最大问题是如果系统是单机系统,就会出现因系统升级或硬件故障而下线。此外,还有一个问题是基于纵向扩展设计的系统的瓶颈性显而易见,因此纵向扩展的扩展性能甚为堪忧。
但是,这并不说明纵向扩展不再有价值,实际上,在真正的商业环境中,很多高可用系统的架构都采用主从热备份、多机互为热备份、分布式共识等架构,它们往往比单纯的横向扩展系统有更为简捷的架构逻辑,更易于维护,性价比也更高,甚至有更高的系统算力——是的,你没有看错,很多所谓的“水平分布式系统”的算力都非常糟糕。其中最核心的逻辑在于,在一个实例的CPU并发能力都没有得到充分利用的前提下就贸然地堆叠更多的实例,并不能获得更高的并发算力,因为多实例间的网络通信会让系统性能呈指数级下降。
构建分布式系统的第一原则就是:先纵向扩展,再横向扩展。这个和先学会走再学习跑是一个道理。纵向扩展与横向扩展之间的关系并非是从0到1的关系,它们之间还有0.5、0.25、0.75。打个具体的比方,单机系统可以升级为多机热备份系统,多机热备份可以升级为分布式共识系统,分布式共识系统再继续扩展为多级分布式共识系统,直至在可行的条件下拓展为大规模水平分布式系统。而在真正的商业环境下,大规模水平分布式系统不但极为少见,而且系统效率往往并不高。在全球范围内,只有在极为特殊的情境下,大规模水平分布式系统才能物尽其用。换而言之,为了追求“水平”分布式而部署水平分布式是盲目且没有价值的行为。
⑥单点失效是可以容忍的:可扩展系统构建中应避免任何可能出现的单点故障。从存储、网络、计算的IaaS层一直到应用层,应当尽可能全部实现高可用性。
⑦无状态与解耦合无关紧要:无状态是架构与服务解耦的先决条件。典型的有状态服务是传统的数据库服务,每一笔交易会有多次操作,前后之间存在状态依赖性,以实现数据的一致性。而无状态服务没有这种强一致性和依赖性要求,因而可以更容易实现分布式处理和并行处理。无状态+解耦合是微服务架构的核心理念——构建云计算或大数据服务的主要原则。
⑧只要关系数据库:与这个观点相对应的一个极端是RDBMS彻底无用论。此二者都是非此即彼,尤其在大数据时代,RDBMS独立难支,但是没有RDBMS也是有失偏颇的。除了少数非常专一的应用与服务只需要某种单一数据库——例如键值数据库NoSQL,多数系统需要两种或多种数据库来丰富数据处理能力。
⑨数据复制无用论:这个误区呼应第一个误区。可扩展应用及基础架构中解决的最核心的问题是数据的高可用性和可扩展性,这两者主要是通过数据复制来实现的,例如数据库系统中的读复制以及分片处理技术。
要衡量一个系统的可扩展性,通常可以从以下5个维度来进行:
①负载:负载可扩展性是衡量分布式系统能力最主要的指标,它主要关注系统随着负载增/减的可伸缩性(弹性)。在第一、第二平台时代,分布式系统的主要方式是纵向扩展;在第三平台时代,分布式系统的主要方式是横向扩展。
②功能:系统实现并提供新功能的代价与便捷性。
③跨区域:系统支持广泛区域用户与负载的能力。例如,从一个区域的数据中心延展到另一个或多个区域的数据中心,以更好就近支持本地负载的这种能力。
④管理:系统管理的安全性与便捷性,以及支持新客户的能力。例如,对多租户环境的支持,如何在同一物理架构上实现良好的数据安全性与一致性支持等。
⑤异构:异构可扩展性主要是指对不同硬件配置的支持性,其中包括系统硬件升级、对不同供应商硬件的无缝继承等。
如果从数学(算法)角度来衡量可扩展系统,那么我们通常采用的是时间复杂度。如果一个系统对资源(计算、网络、存储等)的需求与输入(服务的节点、用户数等)的关系符合,则我们认为该系统具有可扩展性;反之如果是
或更高阶的关系,则该系统不具有可扩展性。
在网络领域(如路由协议),当路由器节点增加时,路由列表长度(内存资源)与路由节点之间的关系是O(lgN),即随着路由节点的增加,路由列表增长趋缓(增长曲线趋平),则该路由协议具有可扩展性。
以点对点文件分享网络(服务)的发展为例,最早的内容分享系统Napster的设计架构是一台中央索引服务器,该服务器负责收集每个加入节点的可分享文件列表,这样的设计导致系统存在单一故障点,也容易被黑客攻击或招来法律诉讼。有鉴于Napster的法律诉讼败诉,随后出现的Gnutella解决了Napster的单点失效问题,推出了纯分布式点对点文件分享服务。在早期阶段,Gnutella的在线文件查询设计采用泛洪查询模式,即一个节点发出的文件搜索请求会分发给网络内其他所有节点,从而导致在整个网络规模超过一定规模后多数查询会因超时而失败(这种扩展性改革虽然避免了单点失效,但系统效率被降低了)。后来Gnutella采用了分布式哈希表,它的设计原则是网络中每个节点只需要与另外的lgN个节点互动来生成分布式哈希表中的键值,因此系统可扩展到容纳数以百万计的节点的这种方案才解决了文件查询可扩展性的问题。当然,在纯分布式P2P系统中,分布式哈希表所面临的问题是查询关键字只支持完全匹配,不支持模糊匹配、模式查询。这一问题在混合式P2P系统中得到了解决,例如Skype音视频服务系统采用的是两级用户架构——超级节点+普通节点,其中超级节点负责建立查询索引与普通节点间路由,普通节点则是用户终端。图4-1展示的是Skype的混合P2P架构。混合P2P架构在实践中既解决了可扩展性不足(存在单点失效)的问题,也解决了纯分布式系统的信息索引与查询困难的问题。

一套完整的云应用/服务架构通常可以被分为负载均衡层、应用服务层、缓存服务层数据库服务层以及云存储层。云应用/服务的5层架构如图2所示。

下面我们主要介绍负载均衡层、应用服务层和缓存服务层。
(1)负载均衡层
负载均衡层是5层架构中最先面对用户的,也是相对容易实现的。通常为了避免单点失效,至少设置两台负载均衡服务器(通常在两台物理主机之上,以避免单机硬件故障导致的单点失效)。整个扩展设置过程通常可以完全自动化,例如通过DNS API来配置新增或删除负载均衡节点。常见的负载均衡解决方案有HAProxy和Nginx,它们通常可以支持跨云平台的负载均衡,即服务器及其他层跨云。当然这种架构的设计与实现复杂度会急剧增高,云爆发就是典型的跨云基础架构模式。智能的负载均衡层能做到根据应用服务器层的健康状态和负载状态动态引流,以确保系统真正实现均衡的负载。
云服务提供商通常会提供现成的负载均衡服务,例如AWS的ELB、RackSpace的CLB,还有VMware vCloud Air Gateway Services,它们都提供强大的负载均衡服务。
(2)应用服务层
负载均衡层之下就是应用服务层,它负责处理负载均衡层转发的用户请求,并返回相应的数据集。通常数据集分为静态数据与动态数据,前者大抵可以被保留在缓存服务层,以降低应用服务器及数据库服务器的负载(见图2),并加快客户端获得返回数据的速度;后者通常需要数据库服务层的配合来动态生成所需数据集。本层的扩展经常通过对现有服务器的负载进行监控(主要是CPU,其次为内存、网络、存储空间),并根据需要进行横向扩展(即水平扩展)或纵向扩展(即垂直扩展)来实现。横向扩展通常是上线同构的服务器(物理机或虚拟机),以降低现有服务器或服务器集群负载;纵向扩展则对CPU、内存、网络、存储空间等进行升级。横向扩展通常不需要系统下线,但是纵向扩展要求被升级的主机(可能是虚拟机或容器)重启。
应用服务层涉及应用服务逻辑,因而当多台服务器协同工作时,还需要确保它们之上所运行服务的一致性。DevOps通常作为PaaS层一部分任务或数据中心的管理与编排组件,实现一致化的应用升级和部署。
(3)缓存服务层
缓存服务层既可能存在于应用服务器层与数据库服务器层之间,也可能在应用服务器层之上,前者可以被用来降低数据库服务器的重复计算量与网络负载,后者则被用来降低应用服务器负载与网络带宽消耗。缓存技术的应用范围极广,从Web服务器到中间件再到数据库都大量使用缓存,以降低不必要的重复计算,进而提高系统的综合性能。
缓存服务层的扩展性实现在避免出现单点失效的基础之上,单个节点的缓存服务器/应用服务器共享节点的方式在生产环境中都是不可取的,主要问题是如何实现多缓存节点间的负载均衡。常见的分布式简单缓存实现是Memcached(全球较为繁忙的20个网站中有18个使用了Memcached),多台Memcached服务器间形成了一张哈希表,当有新的缓存节点加入或旧的节点被删除(或下线)时,现有节点并不需要全部进行大规模改动来生成新的哈希表(这种方法被称作一致性哈希算法)。对于哈希表中数据的替换与更新,Memcached采用的是生存时间值(Time to Live,TTL)与最近最少使用(Least Recently Used,LRU)模式。对于更复杂的高扩展性缓存系统的设计来说,属于NoSQL类的CouchBase数据库提供了更为健全的企业级缓存功能的实现方式,例如数据常存、自动负载均衡、多租户支持等。
(文/Ricky - HPC高性能计算与存储专家、大数据专家、数据库专家及学者)
· END ·