如何构建高可用的分布式系统

发布网友 发布时间:2022-04-22 01:43

我来回答

2个回答

懂视网 时间:2022-05-06 12:12

互联网等系统对于可用性都非常的重视,构建一个高可用的系统,有些常用的招数,在这里简单的说下,其实多数都是靠各互联网公司在实战中摸爬滚打积累出来的“血泪”经验。 1. 监控和报警 没有监控和报警的在线系统,就像是开着一辆没有仪表盘的车一样,因此如

互联网等系统对于可用性都非常的重视,构建一个高可用的系统,有些常用的招数,在这里简单的说下,其实多数都是靠各互联网公司在实战中摸爬滚打积累出来的“血泪”经验。

1. 监控和报警
没有监控和报警的在线系统,就像是开着一辆没有仪表盘的车一样,因此如果没有监控和报警,其他一切都是浮云,这点说起来容易,做起来却是比较折腾的,例如监控点需要有哪些,怎么报警是合适的。

这个的评估比较容易,就是故障发生是不是都是监控和报警先发现的,故障发现率是衡量这件事做的咋样的一个不错的指标。

2. SPoF(Single Point of Failure)
这是高可用系统中最不允许的现象,应用如果只部署在一台机器上,就意味着只要这台机器出现问题,就不可用了,为了避免这个问题,通常会采用cluster、主备的方式,实在不好做的情况下才用主备,例如带状态的DB等,而对于不带状态的,最好还是用cluster的方式,因为主备方式实现较麻烦,另外不具备伸缩性。

单台机器这只是SPoF中的一个狭义的点,更放大看还会有单框机器(如果是刀框的话)、单个机柜、单个网络核心路由、单个机房、单个城市,可见一个真正的高可用系统要解决的技术问题是不少的,并且是要付出较高的成本的。

3. 解耦
业务逻辑在实现上总是会有很多关键的逻辑,还有一些附加的逻辑,例如在做完操作后要发个短信通知什么的,如果把这些逻辑也放到主逻辑过程中一起实现,可能会出现的问题就是这些边缘逻辑出问题,然后导致主逻辑挂了,因此在实现系统时需要做一定的解耦,关键的逻辑同步完成,而非关键逻辑则通过异步的消息系统来完成,这是在设计高可用系统时一个非常关键的点。

除了后端这种实现的解耦外,前端页面的构成其实也需要考虑好解耦,例如一个页面上,有些内容即使不显示也不会出什么问题的,对于这些内容应该通过ajax等方式来实现和主要内容解耦。

4. 隔离
一个业务系统,必然会涵盖多种多样的功能,而这些功能中必然会有重要的和不重要的,例如上面解耦后页面上有些不重要的内容会通过ajax来异步获取,而如果这个不重要的内容的生成和重要的内容生成是同一个系统的话,有可能会出现不重要的内容生成的代码处理慢,从而导致把共同的处理线程池的所有线程耗满。

为了解决这个问题,通常可以采用两种办法,一是拆分系统,直接把重要的和不重要的拆成两个应用,二是通过七层路由来分到不同的机器上,也可以是域名,这样系统仍然是同一个,七层路由对性能损害很大,慎用(除非是类似基于zk实现的软负载,然后在客户端执行的七层路由)。

除了重要功能和不重要功能外,还会出现耗资源和相对不怎么耗资源(包括很多种,例如DB连接..)、给重要用户和普通用户,对于这些情况,都需要采用上面的两种办法来做到隔离。

5. 容灾
一个在线运行的系统,不可避免的要面对各种灾难事件,而一个高可用的系统,必须做到在各种灾难事件面前坚挺的活着,为了做到这个,需要有N多的措施。

通常系统都会依赖到其他的一些系统,而这个时候首先要做的就是超时控制,看过N多的case,都是由于没有设置超时时间,从而在依赖的系统响应变慢的情况下,自己的系统的所有处理线程也被拖S的现象,因此所有的阻塞的wait的地方都一定要是带超时的,在线系统更能接受的是失败,响应慢绝对是在线系统的噩梦。

除了超时外,在代码的实现上需要做一些自动降级的策略,有些时候调用的这个后端系统可能不是那么关键的逻辑,那么在这种情况下,应支持自动的降级,当后端系统出现超时等问题的时候,直接忽略掉,例如很多应用都会有待读的消息数,当读不到的时候,其实不显示关系也不大,因此此时可以自动降级掉。

除了自动降级外,还需要有多种手动降级的策略,例如一个页面上的很多功能点都需要支持手工关闭的开关,这样可以在某些系统出现问题,或压力大时可以直接关闭掉,降低系统压力,一种典型的例子是例如高清晰的图片会消耗掉很多的带宽,如果带宽紧急的情况下,应该支持显示更低质量一点的图片,当然,这是有一定损害的降级,但相比系统全挂,显然是这个方式好。

自恢复能力也是系统设计时的重要考虑点,例如当依赖的系统出问题到恢复后,系统本身也要能自恢复,例如一个最简单的是依赖的一个系统挂了,不能说还得重启系统本身才能恢复。

自我保护能力是容灾中的重要点,例如需要处理的请求超过了处理能力,那这个时候应该拒绝,要做到这点,必须首先知道系统的处理能力到底是多少(小声的说一句:所有的模拟的压力测试都是无法反应系统的处理能力的,要拿到系统的处理能力必须是用真实的访问来测试,原因是数据、用户行为其实是无法模拟的或者说难度非常非常的高),除了处理能力的保护外,还应该做负载层面的保护,以避免某些情况下即使在处理能力范围内,负载出现飙高导致无法登陆机器处理故障的现象。

除了上面说的这些系统层面的策略外,容灾还需要考虑机房层面的容灾、地域的容灾。

从上面5点来说,要做到一个高可用的系统真心不容易,从编程技巧、系统设计以及基础设施(IDC)建设都需要作出巨大的努力,并且要付出巨大的成本,而对于一个高访问量的大规模系统而言,基本上所有认为不太可能发生的事其实都是会发生的,如果没有准备好的话,瞬间可用率就会大幅损失。

热心网友 时间:2022-05-06 09:20

  开源软件已经成为许多大型网站的基本组成部分,随着这些网站的逐步壮大,他们的网站架构和一些指导原则也出现在开发者们的面前,给予切实有用的指导和帮助。本文旨在介绍一些核心问题以及通过构建模块来制作大型网站,实现最终目标。   这篇文章主要侧重于Web系统,并且也适用于其他分布式系统。   Web分布式系统设计的原则   构建并运营一个可伸缩的Web站点或应用程序到底指的是什么?在最初,仅是通过互联网连接用户和访问远程资源。   和大多数事情一样,当构建一个Web服务时,需要提前抽出时间进行规划。了解大型网站创建背后的注意事项以及权衡可能会给你带来更加明智的决策,当你在创建小网站时。下面是设计大型Web系统时,需要注意的一些核心原则:   1.可用性   2.性能   3.可靠性   4.可扩展   5.易管理   6.成本   上面的这些原则给设计分布式Web架构提供了一定的基础和理论指导。然而,它们也可能彼此相左,例如实现这个目标的代价是牺牲成本。一个简单的例子:选择地址容量,仅通过添加更多的服务器(可伸缩性),这个可能以易管理(你不得不操作额外的服务器)和成本作为代价(服务器价格)。   无论你想设计哪种类型的Web应用程序,这些原则都是非常重要的,甚至这些原则之间也会互相羁绊,做好它们之间的权衡也非常重要。   基础   当涉及到系统架构问题时,这几件事情是必须要考虑清楚的:什么样的模块比较合适?如何把它们组合在一起?如何进行恰当地权衡?在扩大投资之前,它通常需要的并不是一个精明的商业命题,然而,一些深谋远虑的设计可以帮你在未来节省大量的时间和资源。   讨论的重点几乎是构建所有大型Web应用程序的核心:服务、冗余、分区和故障处理能力。这里的每个因素都会涉及到选择和妥协,特别是前面所讨论的那些原则。解释这些核心的最佳办法就是举例子。   图片托管应用程序   有时,你会在线上传图片,而一些大型网站需要托管和传送大量的图片,这对于构建一个具有成本效益、高可用性并具有低延时(快速检索)的架构是一项挑战。   在一个图片系统中,用户可以上传图片到一个*服务器里,通过网络连接或API对这些图片进行请求,就像Flickr或者Picasa。简单点,我们就假设这个应用程序只包含两个核心部分:上传(写)图片和检索图片。图片上传时最好能够做到高效,传输速度也是我们最关心的,当有人向图片发出请求时(例如是一个Web页面或其他应用程序)。这是非常相似的功能,提供Web服务或内容分发网络(一个CDN服务器可以在许多地方存储内容,所以无论是在地理上还是物理上都更加接近用户,从而导致更快的性能)边缘服务器。   该系统需要考虑的其他重要方面:   1.图片存储的数量是没有*的,所以存储应具备可伸缩,另外图片计算也需要考虑   2.下载/请求需要做到低延迟   3.用户上传一张图片,那么图片就应该始终在那里(图片数据的可靠性)   4.系统应该易于维护(易管理)   5.由于图片托管不会有太高的利润空间,所以系统需要具备成本效益   图1是个简化的功能图   图1 图片托管系统的简化结构图   在这个例子中,系统必须具备快速、数据存储必须做到可靠和高度可扩展。构建一个小型的应用程序就微不足道了,一台服务器即可实现托管。如果这样,这篇文章就毫无兴趣和吸引力了。假设我们要做的应用程序会逐渐成长成Flickr那么大。   服务   当我们考虑构建可伸缩的系统时,它应有助于解耦功能,系统的每个部分都可以作为自己的服务并且拥有清晰的接口定义。在实践中,这种系统设计被称作面向服务的体系结构(SOA)。对于此类系统,每个服务都有它自己的独特功能,通过一个抽象接口可以与外面的任何内容进行互动,通常是面向公众的另一个服务 API。   把系统分解成一组互补性的服务,在互相解耦这些操作块。这种抽象有助于在服务、基本环境和消费者服务之间建立非常清晰的关系。这种分解可以有效地隔离问题,每个块也可以互相伸缩。这种面向服务的系统设计与面向对象设计非常相似。   在我们的例子中,所有上传和检索请求都在同一台服务器上处理。然而,因为系统需要具备可伸缩性,所以把这两个功能打破并集成到自己的服务中是有意义的。   快进并假设服务正在大量使用;在这种情况下,很容易看到写图片的时间对读图片时间有多大影响(他们两个功能在彼此竞争共享资源)。根据各自体系,这种影响会是巨大的。即使上传和下载速度相同(这是不可能的,对于大多数的IP网络来说,下载速度:上传速度至少是3:1),通常,文件可以从缓存中读取,而写入,最终是写到磁盘中(也许在最终一致的情况下,可以被多写几次)。即使是从缓存或者磁盘(类似SSD)中读取,数据写入都会比读慢(Pole Position,一个开源DB基准的开源工具和结果)。   这种设计的另一个潜在问题是像Apache或者Lighttpd这些Web服务器通常都会有一个并发连接数上限(默认是500,但也可以更多),这可能会花费高流量,写可能会迅速消掉所有。既然读可以异步或利用其他性能优化,比如gzip压缩或分块传输代码,Web服务可以快速切换读取和客户端来服务于更多的请求,超过每秒的最大连接数(Apache的最大连接数设置为500,这种情况并不常见,每秒可以服务几千个读取请求)。另一方面,写通常倾向于保持一个开放的链接进行持续上传,所以,使用家庭网络上传一个1 MB的文件花费的时间可能会超过1秒,所以,这样的服务器只能同时满足500个写请求。   图2:读取分离   规划这种瓶颈的一个非常好的做法是把读和写进行分离,如图2所示。这样我们就可以对它们单独进行扩展(一直以来读都比写多)但也有助于弄明白每个点的意思。这种分离更易于排除故障和解决规模方面问题,如慢读。   这种方法的优点就是我们能够彼此解决问题——在同种情况下,无需写入和检索操作。这两种服务仍然利用全球语料库的图像,但是他们可以自由地优化性能和服务方法(例如排队请求或者缓存流行图片——下面会介绍更多)。从维护和成本角度来看,每一个服务都可以根据需要进行扩展,但如果把它们进行合并或交织在一起,那么有可能无意中就会对另一个性能产生影响,如上面讨论的情景。   当然,如果你有两个不同的端点,上面的例子可能会运行的很好(事实上,这非常类似于几个云存储供应商之间的实现和内容分发网络)。虽然有很多种方法可以解决这些瓶颈,但每个人都会有不同的权衡,所以采用适合你的方法才是最重要的。   例如,Flickr解决这个读/写问题是通过分发用户跨越不同的碎片,每个碎片只能处理一组用户,但是随着用户数的增加,更多的碎片也会相应的添加到群集里(请参阅Flickr的扩展介绍)。在第一个例子中,它更容易基于硬件的实际用量进行扩展(在整个系统中的读/写数量),而Flickr是基于其用户群进行扩展(but forces the assumption of equal usage across users so there can be extra capacity)。而前面的那个例子,任何一个中断或者问题都会降低整个系统功能(例如任何人都没办法执行写操作),而Flickr的一个中断只会影响到其所在碎片的用户数。在第一个例子中,它更容易通过整个数据集进行操作——例如,更新写服务,包括新的元数据或者通过所有的图片元数据进行搜索——而 Flickr架构的每个碎片都需要被更新或搜索(或者需要创建一个搜索服务来收集元数据——事实上,他们就是这样做的)。   当谈到这些系统时,其实并没有非常正确的答案,但有助于我们回到文章开始处的原则上看问题。确定系统需求(大量的读或写或者两个都进行、级别并发、跨数据查询、范围、种类等等),选择不同的基准、理解系统是如何出错的并且对以后的故障发生情况做些扎实的计划。   冗余   为了可以正确处理错误,一个Web架构的服务和数据必须具备适当的冗余。例如,如果只有一个副本文件存储在这台单独的服务器上,那么如果这台服务器出现问题或丢失,那么该文件也随即一起丢失。丢失数据并不是什么好事情,避免数据丢失的常用方法就是多创建几个文件或副本或冗余。   同样也适用于服务器。如果一个应用程序有个核心功能,应确保有多个副本或版本在同时运行,这样可以避免单节点失败。   在系统中创建冗余,当系统发生危机时,如果需要,可以消除单点故障并提供备份或备用功能。例如,这里有两个相同的服务示例在生产环境中运行,如果其中一个发生故障或者降低,那么该系统容错转移至那个健康的副本上。容错转移可以自动发生也可以手动干预。   服务冗余的另一重要组成部分是创建一个无共享架构。在这种体系结构中,每个节点都能相互运行,并且没有所谓的*“大脑”管理状态或协调活动其他节点。这对系统的可扩展帮助很大,因为新节点在没有特殊要求或知识的前提下被添加。然而,最重要的是,这些系统是没有单点故障的,所以失败的弹性就更大。   例如在我们的图片服务器应用程序中,所有的图片在另一个硬件上都有冗余副本(理想情况下是在不同的地理位置,避免在数据中心发生一些火灾、地震等自然事故),服务去访问图片将被冗余,所有潜在的服务请求。(参见图3:采用负载均衡是实现这点的最好方法,在下面还会介绍更多方法)   图3 图片托管应用程序冗余   分区   数据集有可能非常大,无法安装在一台服务器上。也有可能这样,某操作需要太多的计算资源、性能降低并且有必要增加容量。在这两种情况下,你有两种选择:纵向扩展或横向扩展。   纵向扩展意味着在单个服务器上添加更多的资源。所以,对于一个非常大的数据集来说,这可能意味着添加更多(或更大)的硬件设备,来使一台服务器能容下整个数据集。在计算操作下,这可能意味着移动计算到一个更大的服务器上,拥有更快的CPU或更大的内存。在各种情况下,纵向扩展可以通过提升单个资源的处理能力来完成。   横向扩展在另一方面是添加更多的节点,在大数据集下,这可能会使用第二服务器来存储部分数据集,对于计算资源来说,这意味着分割操作或跨节点加载。为了充分利用横向扩展,它应作为一种内在的系统架构设计原则,否则修改或拆分操作将会非常麻烦。   当谈到横向扩展时,最常见的做法是把服务进行分区或碎片。分区可以被派发,这样每个逻辑组的功能就是的。可以通过地理界限或其他标准,如非付费与付费用户来完成分区。这些方案的优点是他们会随着容量的增加提供一个服务或数据存储。   在我们的图片服务器案例中,用来存储图片的单个文件服务器可能被多个文件服务器取代,每个里面都会包含一套自己独特的图像。(见图4)这种架构将允许系统来填充每一个文件/图片服务器,当磁盘填满时会添加额外的服务器。这样的设计需要一个命名方案,用来*图片文件名到其相应的服务器上。图像名字可以形成一个一致的哈希方案并映射到整个服务器上;或者给每张图片分配一个增量ID,当客户端对图片发出请求时,图片检索服务只需要检索映射到每个服务器上(例如索引)的ID。   图4 图片托管应用程序冗余和分区   当然,跨越多个服务器对数据或功能进行分区还是有许多挑战的。其中的关键问题是数据本地化。在分布式系统中,数据操作或计算点越接近,系统性能就会越好。因此,它也可能是个潜在问题,当数据分散在多个服务器上时。有时数据不是在本地,那么就要迫使服务器通过网络来获取所需的信息,这个获取的过程就会设计到成本。   另一潜在问题是不一致。当这里有多个服务对一个共享资源执行读写操作时,潜在可能会有另一个服务器或数据存储参与进来,作为竞选条件——一些数据需要更新,但是读的优先级高于更新——在这种情况下,数据就是不一致的。例如在图片托管方案中,有可能出现的不一致是:如果一个客户端发送更新“狗”图片请求,进行重新命名,把“Dog”改成“Gizmo”,但同时,另一个客户端正在读这张图片。在这种情况下,标题就是不清楚的。“Dog”或“Gizmo” 应该被第二个客户端接收。   当然,在进行数据分区时会产生一些障碍,但是分区允许把每个问题拆分到管理群里——通过数据、负载、使用模式等。这样对可扩展和易管理都是有帮助的,但也不是没有风险的。这里有很多方式来降低风险和故障处理;然而,为了简便起见,并未在本文中详细说明,如果你有兴趣,可以访问我的博客。   总结   以上介绍的都是设计分布式系统需要考虑的核心要素。可用性、性能、可靠性、可扩展、易管理、成本这几个原则非常重要,但在实际应用中可能会以牺牲某个原则来实现另外一个原则,在这个过程中就要做好权衡工作,做到因时制宜。   在下面的构建分布式系统实战中,我们将会深入介绍如何设计可扩展的数据访问,包括负载均衡、代理、全局缓存、分布式缓存等。   英文地址:Dr.Dobb's   文:CSDN

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com