可伸缩 Web 架构与分布式系统

1.2: 读写分离

 

将图片的读、写操作拆分成各自的服务是一个应对这种瓶颈很好的解决方案,如图1.2。这样允许我们能够独立的扩展它们(我们通常会读大于写),而且有助于将每一点的进展情况看得更加清晰。最后,这样可以分离未来的担心,可以更简单地解决像读操作缓慢的问题,并做到可伸缩。

这种方法的好处在于我们能够独立(不影响其他)解决问题——我们不用担心在同一上下文中写入、读取新的图片。这两者(服务)仍然影响着全部的图片,但均能通过service-appropriate方法优化它们的性能(比如让请求排队,或者缓存受欢迎的图片——更多种方式请见下文)。从一个维护和成本的视角出发,每个服务均能独立、按需伸缩是非常好的,因为如果它们被组合、混合在一起,在上面讨论的场景下,可能某一(服务)不经意间就会影响到其他(服务)的性能。

当然,当你考虑着两个不同点时,上面的例子能够工作得很好(事实上,这跟一些云存储提供商的实现方案和CDN很类似)。尽管还有很多方法来处理这些类型的瓶颈,但每个都有不同方面的权衡。

例如,Flickr通过将用户分布在不同区域的方法来解决读/写问题,比如每个分区只处理一定数量的用户,随着用户的增加,集群会更多的分区(参考Flickr可伸缩报告)。在第一个例子中,基于实际使用(整个系统的读写操作数量)可以更容易地伸缩硬件,然而Flickr是基于它的用户(但强制假设用户的使用率均等,所以仍有额外的容量)。对于前者来说,停电或者一个服务的问题就会降低整个系统的功能性(比如没人可以写入文件),然而Flickr的一个分区停电仅会影响到这个分区相应的用户。第一个例子易于操作整个数据集,比如升级写入服务来包含新的元数据或者搜索所有的图片元数据,然而在Flickr的架构下,每个分区均需要被更新或搜索(或者一个搜索服务需要能够整理相关元数据——事实上他们确实这么做)。

对于这些系统来说没有孰对孰错,而是帮助我们回到本章开头所说的准则,判断系统需求(读多还是写多还是两者都多,并发程度,跨数据集查询,搜索,排序等),检测不同的取舍,理解系统为什么会失败并且有可靠的计划来应对失败的发生。

冗余

为了能够优雅地处理失败问题,Web架构必须做到服务和数据的冗余。比如,如果在单台服务器上仅有一份文件,那么失去那台服务器就意味着丢失那份文件。丢失数据很少是件好事情,而通常的解决方案是创建多个、冗余的备份。


该准则同样适用于服务。如果应用有一个核心功能,那么通过确保多个拷贝(多个同类服务实例)或者版本同时运行能够免于单点失败的情况。

在一个系统中创建冗余能够去除单点失败,并提供一个备份或在必要的紧急时刻替换功能。例如,如果在生产环境有同一服务的两个实例在运行,其中一个失败或者降级了,系统可以(启动)failover到那个健康状态的服务。Failover可以自动发生或者需要人工干预。

服务冗余的另一个关键点在于创建一个非共享的架构(译者理解为无状态架构)。通过这种架构,每个节点都能够独立操作,并且没有中央大脑来管理状态或者协调其他节点的活动。这对于可伸缩性非常有帮助,因为新的节点不需要特殊的条件或知识就能加入(到集群)。但是,最重要的是在这些系统中不会存在单点失败问题,所以它们能够更加弹性地面对失败。

例如,在我们的图片服务应用,所有的图片会在另一个地方的硬件中有冗余的备份(理想情况是在一个不同的地理位置,以防地震或者数据中心火灾这类的灾难发生),而访问图片的服务同样是冗余的,所有(服务)都可能会服务请求。(见图1.3)(负载均衡器可以将其变为现实,详情请见下文。)

 

1.3:图片托管应用,带有冗余特性

 

分区

单台服务器可能没法放下海量数据集。也可能是一个操作需要太多计算资源,消耗性能,使得有必要增加(系统)容量。无论是哪种情况,你都有两种选择:垂直扩展(scale vertically)或者水平扩展(scale horizontally)。

垂直扩展意味着在单台服务器上增加更多的资源。所以对于大数据来说,这意味着增加更多(更大容量)的硬盘以便让单台服务器能够容纳整个数据集。对于计算操作的场景,这意味着将计算任务交给一台拥有更快CPU或者更多内存的大型服务器。对于每种场景,垂直扩展是通过自身(个体)能够处理更多的方式来达到目标的。

另一方面,对于水平扩展来说,就是增加更多的节点。对于大数据集,可能是用另一台服务器来存储部分数据集;而对于计算资源来说,则意味着将操作进行分解或者加载在一些额外的节点上。为了充分利用水平扩展的优势,这(译者认为此处指代的是系统支持水平扩展。垂直扩展对于应用来说无需修改,通常升级机器即可达到目的。而水平扩展就要求应用架构能够支持这种方式的扩展,因为数据、服务都是分布式的,需要从软件层面来支持这一特性,从而做到数据、服务的水平可扩展。)应该被天然地包含在系统架构设计准则里,否则想要通过修改、隔离上下文来达到这一点将会相当麻烦。

对于水平扩展来说,通常方法之一就是将你的服务打散、分区。分区可以是分布式的,这样每个逻辑功能集都是分离的;分区可通过地理边界来划分,或者其他标准如付费/未付费用户。这些设计的好处在于它们能够使得服务或数据存储易于增加容量。

在我们的图片服务器例子中,可以将单台存储图片的服务器替换为多台文件服务器,每台保存各自单独的图片集。(见图1.4)这样的架构使得系统能够往各台文件服务器中存入图片,当磁盘快满时再增加额外的服务器。这种设计将需要一种命名机制,将图片的文件名与所在服务器关联起来。一个图片的名字可以通过服务器间一致性Hash机制来生成。或者另一种选择是,可以分配给每张图片一个增量ID,当一个客户端请求一张图片时,图片检索服务只需要维护每台服务器对应的ID区间即可(类似索引)。

 

1.4:图片托管应用,加入冗余和分区特性

 

当然,将数据或功能分布在多台服务器上会带来很多挑战。关键问题之一是数据局部性(data locality);在分布式系统里,数据离操作或者计算点越近,系统性能就越高。因此将数据分布在多台服务器可能是有问题的,任何需要(数据)的时候都可能不在本地,使得服务器必须通过网络来获取所需的信息。

 

另一个潜在问题是不一致性。当不同的服务在对同一块共享资源进行读、写时,可能是另一个服务或者数据,就会存在竞争条件的机会——当一些数据将被更新,但读操作先于更新发生——这类场景下数据就会发生不一致。例如,在图片托管这个场景下,竞争条件会发生在一个客户端发出将小狗图片标题由“Dog”更新为“Gizmo”的请求,但同时另一个客户端正在读取该图片这样的情况下。在这样的情况下,第二个客户端就不清楚接收到的标题会是“Dog”还是“Gizmo”。(此端译者并不理解作者原意,因为无论是在分布式还是在单机环境下,都可能出现同时读、写的操作,返回结果取决于底层存储对同时接收到的请求处理的调度,可能读先于写,反之亦然,故此处的例子用来解释不一致是否并不恰当?)

诚然,关于数据分区还存在很多阻碍,但分区通过数据、负载、用户使用模式等使得每个问题分解成易处理的部分。这样有助于可伸缩性和可管理型,但也不是没有风险的。有很多方法能够用来降低风险、处理失败问题;但为了简化篇幅,本章就不覆盖(这些方法)了。如果你有兴趣想了解更多,可以看下我博客上发表的关于容错性和监控的相关文章。

 

 

 

本教程由尚硅谷教育大数据研究院出品,如需转载请注明来源。

猜你喜欢

转载自blog.csdn.net/sggtgfs/article/details/85102196
今日推荐