首先,咱们须要阐述一下为何须要分布式系统,而不是传统的单体架构。也许这对你来讲已经不是什么问题了,可是请容许我在这里从新说明一下。使用分布式系统主要有两方面缘由。html
增大系统容量。咱们的业务量愈来愈大,而要能应对愈来愈大的业务量,一台机器的性能已经没法知足了,咱们须要多台机器才能应对大规模的应用场景。因此,咱们须要垂直或是水平拆分业务系统,让其变成一个分布式的架构。前端
增强系统可用。咱们的业务愈来愈关键,须要提升整个系统架构的可用性,这就意味着架构中不能存在单点故障。这样,整个系统不会由于一台机器出故障而致使总体不可用。因此,须要经过分布式架构来冗余系统以消除单点故障,从而提升系统的可用性。git
固然,分布式系统还有一些优点,好比:程序员
由于软件服务模块被拆分,开发和发布速度能够并行而变得更快;github
系统扩展性更高;web
团队协做流程也会获得改善;算法
……spring
不过,这个世界上不存在完美的技术方案,采用任何技术方案都是“按下葫芦浮起瓢”,都是有得有失,都是一种trade-off。也就是说,分布式系统在消除上述问题的同时,也给咱们带来了其余的问题。所以,咱们须要清楚地知道分布式系统所带来的问题。shell
下面这个表格比较了单体应用和分布式架构的优缺点。数据库
从上面的表格咱们能够看到,分布式系统虽然有一些优点,但也存在一些问题。
架构设计变得复杂(尤为是其中的分布式事务)。
部署单个服务会比较快,可是若是一次部署须要多个服务,部署会变得复杂。
系统的吞吐量会变大,可是响应时间会变长。
运维复杂度会由于服务变多而变得很复杂。
架构复杂致使学习曲线变大。
测试和查错的复杂度增大。
技术能够不少样,这会带来维护和运维的复杂度。
管理分布式系统中的服务和调度变得困难和复杂。
也就是说,分布式系统架构的难点在于系统设计,以及管理和运维。因此,分布式架构解决了“单点”和“性能容量”的问题,但却新增了一堆问题。而对于这些新增的问题,还会衍生出更多的子问题,这就须要咱们不断地用各式各样的技术和手段来解决这些问题。
这就出现了我前面所说的那些架构方式,以及各类相关的管理型的技术方法。这个世界就是这样变得复杂起来的。
从20世纪70年代的模块化编程,80年代的面向事件设计,90年代的基于接口/构件设计,这个世界很天然地演化出了SOA——基于服务的架构。SOA架构是构造分布式计算应用程序的方法。它将应用程序功能做为服务发送给最终用户或者其余服务。它采用开放标准与软件资源进行交互,并采用标准的表示方式。
开发、维护和使用SOA要遵循如下几条基本原则。
但IBM搞出来的SOA很是重,因此对SOA的裁剪和优化历来没有中止过。好比,以前的SOAP、WSDL和XML这样的东西基本上已经被抛弃了,而改为了RESTful和JSON这样的方式。而ESB(Enterprise Service Bus,企业服务总线)这样很是重要的东西也被简化成了Pub/Sub的消息服务……
不过,SOA的思想一直延续着。因此,咱们如今也不说SOA了,而是说分布式服务架构了。
下面是一个SOA架构的演化图。
咱们能够看到,面向服务的架构有如下三个阶段。
20世纪90年代前,是单体架构,软件模块高度耦合。固然,这张图一样也说明了有的SOA架构其实和单体架构没什么两样,由于都是高度耦合在一块儿的。就像图中的齿轮同样,当你调用一个服务时,这个服务会调用另外一个服务,而后又调用另外的服务……因而整个系统就转起来了。可是这本质是比较耦合的作法。
而2000年左右出现了比较松耦合的SOA架构,这个架构须要一个标准的协议或是中间件来联动其它相关联的服务(如ESB)。这样一来,服务间并不直接依赖,而是经过中间件的标准协议或是通信框架相互依赖。这其实就是IoC(控制反转)和DIP(依赖倒置原则)的设计思想在架构中的实践。它们都依赖于一个标准的协议或是一个标准统一的交互方式,而不是直接调用。
而2010年后,出现了微服务架构,这个架构更为松耦合。每个微服务都能独立完整地运行(所谓的自包含),后端单体的数据库也被微服务这样的架构分散到不一样的服务中。而它和传统SOA的差异在于,服务间的整合须要一个服务编排或是服务整合的引擎。就好像交响乐中须要有一个指挥来把全部乐器编排和组织在一块儿。
通常来讲,这个编排和组织引擎能够是工做流引擎,也能够是网关。固然,还须要辅助于像容器化调度这样的技术方式,如Kubernetes。在Martin Fowler 的 Microservices 这篇文章中有详细描述。
微服务的出现使得开发速度变得更快,部署快,隔离性高,系统的扩展度也很好,可是在集成测试、运维和服务管理等方面就比较麻烦了。因此,须要一套比较好的微服务PaaS平台。就像Spring Cloud同样须要提供各类配置服务、服务发现、智能路由、控制总线……还有像Kubernetes提供的各式各样的部署和调度方式。
没有这些PaaS层的支撑,微服务也是很难被管理和运维的。好在今天的世界已经有具有了这些方面的基础设施,因此,采用微服务架构,我认为只是一个时间问题了。
从目前能够获得的信息来看,对分布式服务化架构实践最先的应该是亚马逊。由于早在2002年的时候,亚马逊CEO杰夫·贝索斯(Jeff Bezos)就向全公司颁布了下面的这几条架构规定(来自《Steve Yegge对Google平台吐槽》一文)。
全部团队的程序模块都要经过Service Interface方式将其数据与功能开放出来。
团队间程序模块的信息通讯,都要经过这些接口。
除此以外没有其它的通讯方式。其余形式一律不容许:不能直接链结别的程序(把其余团队的程序当作动态连接库来连接),不能直接读取其余团队的数据库,不能使用共享内存模式,不能使用别人模块的后门,等等。惟一容许的通讯方式是调用Service Interface。
任何技术均可以使用。好比:HTTP、CORBA、Pub/Sub、自定义的网络协议等。
全部的Service Interface,毫无例外,都必须从骨子里到表面上设计成能对外界开放的。也就是说,团队必须作好规划与设计,以便将来把接口开放给全世界的程序员,没有任何例外。
不这样作的人会被炒鱿鱼。
这应该就是AWS(Amazon Web Service)出现的基因吧。固然,前面说过,采用分布式系统架构后会出现不少的问题。好比:
为了克服这些问题,亚马逊这么多年的实践让其能够运维和管理极其复杂的分布式服务架构。我以为主要有如下几点。
分布式服务的架构须要分布式的团队架构。在亚马逊,一个服务由一个小团队(Two Pizza Team不超过16我的,两张Pizza能够喂饱的团队)负责,从前端负责到数据,从需求分析负责到上线运维。这是良性的分工策略——按职责分工,而不是按技能分工。
分布式服务查错不容易。一旦出现比较严重的故障,须要总体查错。出现一个S2的故障,就能够看到每一个团队的人都会上线。在工单系统里能看到,在故障发生的一开始,你们都在签到并自查本身的系统。若是没问题,也要在线待命(standby),等问题解决。(我在《故障处理最佳实践:应对故障》一文中详细地讲过这个事)。
没有专职的测试人员,也没有专职的运维人员,开发人员作全部的事情。开发人员作全部事情的好处是——吃本身的狗粮(Eat Your Own Dog Food) 最微观的实践。本身写的代码本身维护本身养,会让开发人员明白,写代码容易维护代码复杂。这样,开发人员在接需求、作设计、写代码、作工具时都会考虑到软件的长期维护性。
运维优先,崇尚简化和自动化。为了可以运维如此复杂的系统,亚马逊内部在运维上下了很是大的功夫。如今人们所说的DevOps这个事,亚马逊在10多年前就作到了。亚马逊最为强大的就是运维,拼命地对系统进行简化和自动化,让亚马逊作到了能够轻松运维拥有上千万台虚机的AWS云平台。
内部服务和外部服务一致。不管是从安全方面,仍是接口设计方面,不管是从运维方面,仍是故障处理的流程方面,亚马逊的内部系统都和外部系统同样对待。这样作的好处是,内部系统的服务随时均可以开放出来。并且,从第一天开始,服务提供方就有对外服务的能力。能够想像,以这样的标准运做的团队其能力会是什么样的。
在进化的过程当中,亚马逊遇到的问题不少,甚至还有不少几乎没有人会想到的很是生僻的东西,它都一一学习和总结了,并且都解决得很好。
构建分布式系统很是难,充满了各类各样的问题,但亚马逊仍是绝不犹豫地走了下去。这是由于亚马逊想作平台,不是“像淘宝这样的中介式流量平台”,而是那种“能够对外输出能力的平台”。
亚马逊以为本身没有像史蒂夫·乔布斯(Steve Jobs)这样的牛人,不可能作出像iPhone这样的爆款产品,并且用户天生就是众口难调,与其作一个你们都不满意的软件,还不如把一些基础能力对外输出,引入外部的力量来一块儿完成一个用户满意的产品。这其实就是在创建本身的生态圈。虽然在今天看来这个事已经不稀奇了,可是贝索斯早在十五年前就悟到了,实在是个天才。
因此,分布式服务架构是须要从组织,到软件工程,再到技术上的一个大的改造,须要比较长的时间来磨合和改进,并不断地总结教训和成功经验。
咱们再来看一下分布式系统在技术上须要注意的问题。
这主要表如今:
不一样的软件,不一样的语言会出现不一样的兼容性和不一样的开发、测试、运维标准。不一样的标准会让咱们用不一样的方式来开发和运维,引发架构复杂度的提高。好比:有的软件修改配置要改它的.conf文件,而有的则是调用管理API接口。
在通信方面,不一样的软件用不一样的协议,就算是相同的网络协议里也会出现不一样的数据格式。还有,不一样的团队由于用不一样的技术,会有不一样的开发和运维方式。这些不一样的东西,会让咱们的整个分布式系统架构变得异常复杂。因此,分布式系统架构须要有相应的规范。
好比,我看到,不少服务的API出错不返回HTTP的错误状态码,而是返回个正常的状态码200,而后在HTTP Body里的JSON字符串中写着个:error,bla bla error message。这简直就是一种反人类的作法。我实在不明白为何会有众多这样的设计。这让监控怎么作啊?如今,你应该使用Swagger的规范了。
再好比,我看到不少公司的软件配置管理里就是一个key-value的东西,这样的东西灵活到能够很容易地被滥用。不规范的配置命名,不规范的值,甚至在配置中直接嵌入前端展现内容……
一个好的配置管理,应该分红三层:底层和操做系统相关,中间层和中间件相关,最上面和业务应用相关。因而底层和中间层是不能让用户灵活修改的,而是只让用户选择。好比:操做系统的相关配置应该造成模板来让人选择,而不是让人乱配置的。只有配置系统造成了规范,咱们才hold得住众多的系统。
再好比:数据通信协议。一般来讲,做为一个协议,必定要有协议头和协议体。协议头定义了最基本的协议数据,而协议体才是真正的业务数据。对于协议头,咱们须要很是规范地让每个使用这个协议的团队都使用一套标准的方式来定义,这样咱们才容易对请求进行监控、调度和管理。
这样的规范还有不少,我在这就不一一列举了。
对于传统的单体应用,一台机器挂了,整个软件就挂掉了。可是你千万不要觉得在分布式的架构下不会发生这样的事。分布式架构下,服务是会有依赖的,因而一个服务依赖链上,某个服务挂掉了,会致使出现“多米诺骨牌”效应,会倒一片。
因此,在分布式系统中,服务的依赖也会带来一些问题。
这是服务治理的内容了。服务治理不但须要咱们定义出服务的关键程度,还须要咱们定义或是描述出关键业务或服务调用的主要路径。没有这个事情,咱们将没法运维或是管理整个系统。
这里须要注意的是,不少分布式架构在应用层上作到了业务隔离,然而,在数据库结点上并无。若是一个非关键业务把数据库拖死,那么会致使全站不可用。因此,数据库方面也须要作相应的隔离。也就是说,最好一个业务线用一套本身的数据库。这就是亚马逊服务器的实践——系统间不能读取对方的数据库,只经过服务接口耦合。这也是微服务的要求。咱们不但要拆分服务,还要为每一个服务拆分相应的数据库。
在分布式系统中,由于使用的机器和服务会很是多,因此,故障发生的频率会比传统的单体应用更大。只不过,单体应用的故障影响面很大,而分布式系统中,虽然故障的影响面能够被隔离,可是由于机器和服务多,出故障的频率也会多。另外一方面,由于管理复杂,并且没人知道整个架构中有什么,因此很是容易犯错误。
你会发现,对分布式系统架构的运维,简直就是一场噩梦。咱们会慢慢地明白下面这些道理。
运维团队在分布式系统下会很是忙,忙到每时每刻都要处理大大小小的故障。我看到,不少大公司,都在本身的系统里拼命地添加各类监控指标,有的可以添加出几万个监控指标。我以为这彻底是在“使蛮力”。一方面,信息太多等于没有信息,另外一方面,SLA要求咱们定义出“Key Metrics”,也就是所谓的关键指标。然而,他们却没有。这实际上是一种思惟上的懒惰。
可是,上述的都是在“救火阶段”而不是“防火阶段”。所谓“防火胜于救火”,咱们还要考虑如何防火,这须要咱们在设计或运维系统时都要为这些故障考虑,即所谓 Design for Failure。在设计时就要考虑如何减轻故障。若是没法避免,也要使用自动化的方式恢复故障,减小故障影响面。
由于当机器和服务数量愈来愈多时,你会发现,人类的缺陷就成为了瓶颈。这个缺陷就是人类没法对复杂的事情作到事无巨细的管理,只有机器自动化才能帮助人类。 也就是,人管代码,代码管机器,人无论机器!
一般来讲,咱们能够把系统分红四层:基础层、平台层、应用层和接入层。
对于这四层,咱们须要知道:
不少公司都是按技能分工是,把技术团队分为产品开发、中间件开发、业务运维、系统运维等子团队。这样的分工致使各管一摊,不少事情彻底连不在一块儿。整个系统会像 “多米诺骨牌”同样,一个环节出现问题,就会倒下去一大片。由于没有一个统一的运维视图,不知道一个服务调用是如何通过每个服务和资源,也就致使咱们在出现故障时要花大量的时间在沟通和定位问题上。
以前我在某云平台的一次经历就是这样的。从接入层到负载均衡,再到服务层,再到操做系统底层,设置的KeepAlive的参数彻底不一致,致使用户发现,软件运行的行为和文档中定义的彻底不同。工程师查错的过程简直就是一场恶梦,觉得找到了一个,结果还有一个,来来回回花了大量的时间才把全部KeepAlive的参数设置成一致的,浪费了太多的时间。
分工不是问题,问题是分工后的协做是否统一和规范。这点,你必定要重视。
好了,咱们来总结一下今天分享的主要内容。首先,我以亚马逊为例,讲述了它是如何作分布式服务架构的,遇到了哪些问题,以及是如何解决的。我认为,亚马逊在分布式服务系统方面的这些实践和经验积累,是AWS出现的基因。随后分享了在分布式系统中须要注意的几个问题,同时给出了应对方案。
我认为,构建分布式服务须要从组织,到软件工程,再到技术上的一次大的改造,须要比较长的时间来磨合和改进,并不断地总结教训和成功经验。下篇文章中,咱们讲述分布式系统的技术栈。但愿对你有帮助。
正如咱们前面所说的,构建分布式系统的目的是增长系统容量,提升系统的可用性,转换成技术方面,也就是完成下面两件事。
说白了就是干两件事。一是提升总体架构的吞吐量,服务更多的并发和流量,二是为了提升系统的稳定性,让系统的可用性更高。
我们先来看看,提升系统性能的经常使用技术。
缓存系统。加入缓存系统,能够有效地提升系统的访问能力。从前端的浏览器,到网络,再到后端的服务,底层的数据库、文件系统、硬盘和CPU,全都有缓存,这是提升快速访问能力最有效的手段。对于分布式系统下的缓存系统,须要的是一个缓存集群。这其中须要一个Proxy来作缓存的分片和路由。
负载均衡系统,是作水平扩展的关键技术。其能够用多台机器来共同分担一部分流量请求。
异步调用。异步系统主要经过消息队列来对请求作排队处理,这样能够把前端的请求的峰值给“削平”了,然后端经过本身可以处理的速度来处理请求。这样能够增长系统的吞吐量,可是实时性就差不少了。同时,还会引入消息丢失的问题,因此要对消息作持久化,这会形成“有状态”的结点,从而增长了服务调度的难度。
对于通常公司来讲,在初期,会使用读写分离的数据镜像方式,然后期会采用分库分表的方式。
接下来,我们来看看提升系统系统稳定性的一些经常使用技术。
服务拆分,主要有两个目的:一是为了隔离故障,二是为了重用服务模块。但服务拆分完以后,会引入服务调用间的依赖问题。
服务冗余,是为了去除单点故障,并能够支持服务的弹性伸缩,以及故障迁移。然而,对于一些有状态的服务来讲,冗余这些有状态的服务带来了更高的复杂性。其中一个是弹性伸缩时,须要考虑数据的复制或是从新分片,迁移的时候还要迁移数据到其它机器上。
限流降级。当系统实在扛不住压力时,只能经过限流或者功能降级的方式来停掉一部分服务,或是拒绝一部分用户,以确保整个架构不会挂掉。这些技术属于保护措施。
高可用架构,一般来讲是从冗余架构的角度来保障可用性。好比,多租户隔离,灾备多活,或是数据能够在其中复制保持一致性的集群。总之,就是为了避免出单点故障。
高可用运维,指的是DevOps中的CI(持续集成)/CD(持续部署)。一个良好的运维应该是一条很流畅的软件发布管线,其中作了足够的自动化测试,还能够作相应的灰度发布,以及对线上系统的自动化控制。这样,能够作到“计划内”或是“非计划内”的宕机事件的时长最短。
上述这些技术很是有技术含量,并且须要投入大量的时间和精力。
而经过上面的分析,咱们能够看到,引入分布式系统,会引入一堆技术问题,须要从如下几个方面来解决。
服务治理。服务拆分、服务调用、服务发现,服务依赖,服务的关键度定义……服务治理的最大意义是须要把服务间的依赖关系、服务调用链,以及关键的服务给梳理出来,并对这些服务进行性能和可用性方面的管理。
架构软件管理。服务之间有依赖,并且有兼容性问题,因此,总体服务所造成的架构须要有架构版本管理、总体架构的生命周期管理,以及对服务的编排、聚合、事务处理等服务调度功能。
DevOps。分布式系统能够更为快速地更新服务,可是对于服务的测试和部署都会是挑战。因此,还须要DevOps的全流程,其中包括环境构建、持续集成、持续部署等。
自动化运维。有了DevOps后,咱们就能够对服务进行自动伸缩、故障迁移、配置管理、状态管理等一系列的自动化运维技术了。
资源调度管理。应用层的自动化运维须要基础层的调度支持,也就是云计算IaaS层的计算、存储、网络等资源调度、隔离和管理。
总体架构监控。若是没有一个好的监控系统,那么自动化运维和资源调度管理只可能成为一个泡影,由于监控系统是你的眼睛。没有眼睛,没有数据,就没法进行高效的运维。因此说,监控是很是重要的部分。这里的监控须要对三层系统(应用层、中间件层、基础层)进行监控。
流量控制。最后是咱们的流量控制,负载均衡、服务路由、熔断、降级、限流等和流量相关的调度都会在这里,包括灰度发布之类的功能也在这里。
此时,你会发现,要作好这么多的技术,或是要具有这么多的能力,简直就是一个门槛,是一个成本巨高无比的技术栈,看着就都头晕。要实现出来得投入多少人力、物力和时间啊。是的,这就是分布式系统中最大的坑。
不过,咱们应该庆幸本身生活在了一个很是不错的年代。今天有一个技术叫——Docker,经过Docker以及其衍生出来的Kubernetes 之类的软件或解决方案,大大地下降了作上面不少事情的门槛。Docker把软件和其运行的环境打成一个包,而后比较轻量级地启动和运行。在运行过程当中,由于软件变成了服务可能会改变现有的环境。可是不要紧,当你从新启动一个Docker的时候,环境又会变成初始化状态。
这样一来,咱们就能够利用Docker的这个特性来把软件在不一样的机器上进行部署、调度和管理。若是没有Docker或是Kubernetes,那么你能够认为咱们还活在“原始时代”。如今你知道为何Docker这样的容器化虚拟化技术是将来了吧。由于分布式系统已是彻底不可逆转的技术趋势了。
可是,上面还有不少的技术是Docker及其周边技术没有解决的,因此,依然还有不少事情要作。那么,若是是一个一个地去作这些技术的话,就像是咱们在撑开一张网里面一个一个的网眼,本质上这是使蛮力的作法。咱们但愿能够找到系统的“纲”,一把就能张开整张网。那么,这个纲在哪里呢?
总结一下上面讲述的内容,你不难发现,分布式系统有五个关键技术,它们是:
而最后一项——开发和运维的自动化,是须要把前四项都作到了,才有可能实现的。因此,最为关键是下面这四项技术,即应用总体监控、资源和服务调度、状态和数据调度及流量调度,它们是构建分布式系统最最核心的东西。
后面的文章中,我会一项一项地解析这些关键技术。
回顾一下今天的要点内容。首先,我总结了分布式系统须要干的两件事:一是提升总体架构的吞吐量,服务更多的并发和流量,二是为了提升系统的稳定性,让系统的可用性更高。而后分别从这两个方面阐释,须要经过哪些技术来实现,并梳理出其中的技术难点及可能会带来的问题。最后,欢迎你分享一下你在解决系统的性能和可用性方面使用到的方法和技巧。
虽然Docker及其衍生出来的Kubernetes等软件或解决方案,能极大地下降不少事儿的门槛。但它们没有解决的问题还有不少,须要掌握分布式系统的五大关键技术,从根本上解决问题。后面我将陆续撰写几篇文章一一阐述这几大关键技术,详见文末给出的《分布式系统架构的本质》系列文章的目录。
首先,咱们须要一个全栈系统监控的东西。它就像是咱们的眼睛,没有它,咱们就不知道系统到底发生了什么,咱们将没法管理或是运维整个分布式系统。因此,这个系统是很是很是关键的。
而在分布式或Cloud Native的状况下,系统分红多层,服务各类关联,须要监控的东西特别多。没有一个好的监控系统,咱们将没法进行自动化运维和资源调度。
这个监控系统须要完成的功能为:
所谓全栈监控,其实就是三层监控。
基础层:监控主机和底层资源。好比:CPU、内存、网络吞吐、硬盘I/O、硬盘使用等。
中间层:就是中间件层的监控。好比:Nginx、Redis、ActiveMQ、Kafka、MySQL、Tomcat等。
应用层:监控应用层的使用。好比:HTTP访问的吞吐量、响应时间、返回码,调用链路分析,性能瓶颈,还包括用户端的监控。
这还须要一些监控的标准化。
这里还要多说一句,如今咱们的不少监控系统都作得很很差,它们主要有两个很大的问题。
监控数据是隔离开来的。由于公司分工的问题,开发、应用运维、系统运维,各管各的,因此不少公司的监控系统也是各是各的,彻底串不起来。
监控的数据项太多。有些公司的运维团队把监控的数据项多作为一个亮点处处讲,好比监控指标达到5万多个。老实说,这太丢人了。由于信息太多等于没有信息,抓不住重点的监控才会作成这个样子,彻底就是使蛮力的作法。
一个好的监控系统应该有如下几个特征。
关注于总体应用的SLA。主要从为用户服务的API来监控整个系统。
关联指标聚合。 把有关联的系统及其指标聚合展现。主要是三层系统数据:基础层、平台中间件层和应用层。其中,最重要的是把服务和相关的中间件以及主机关联在一块儿,服务有可能运行在Docker中,也有可能运行在微服务平台上的多个JVM中,也有可能运行在Tomcat中。总之,不管运行在哪里,咱们都须要把服务的具体实例和主机关联在一块儿,不然,对于一个分布式系统来讲,定位问题犹如大海捞针。
快速故障定位。 对于现有的系统来讲,故障老是会发生的,并且还会频繁发生。故障发生不可怕,可怕的是故障的恢复时间过长。因此,快速地定位故障就至关关键。快速定位问题须要对整个分布式系统作一个用户请求跟踪的trace监控,咱们须要监控到全部的请求在分布式系统中的调用链,这个事最好是作成没有侵入性的。
换句话说,一个好的监控系统主要是为如下两个场景所设计的。
容量管理。 提供一个全局的系统运行时数据的展现,可让工程师团队知道是否须要增长机器或者其它资源。
性能管理。能够经过查看大盘,找到系统瓶颈,并有针对性地优化系统和相应代码。
定位问题。能够快速地暴露并找到问题的发生点,帮助技术人员诊断问题。
性能分析。当出现非预期的流量提高时,能够快速地找到系统的瓶颈,并能够帮助开发人员深刻代码。
只有作到了上述的这些才能是一个好的监控系统。
下面是我认为一个好的监控系统应该实现的东西。
以下图所示(截图来自我作的一个APM的监控系统)。
服务调用时长分布。使用Zipkin,能够看到一个服务调用链上的时间分布,这样有助于咱们知道最耗时的服务是什么。下图是Zipkin的服务调用时间分布。
服务的TOP N视图。所谓TOP N视图就是一个系统请求的排名状况。通常来讲,这个排名会有三种排名的方法:a)按调用量排名,b) 按请求最耗时排名,c)按热点排名(一个时间段内的请求次数的响应时间和)。
数据库操做关联。对于Java应用,咱们能够很方便地经过JavaAgent字节码注入技术拿到JDBC执行数据库操做的执行时间。对此,咱们能够和相关的请求对应起来。
这样一来,咱们就能够知道服务和基础层资源的关系。若是是Java应用,咱们还要和JVM里的东西进行关联,这样咱们才能知道服务所运行的JVM中的状况(好比GC的状况)。
有了这些数据上的关联,咱们就能够达到以下的目标。
当一台机器挂掉是由于CPU或I/O太高的时候,咱们立刻能够知道其会影响到哪些对外服务的API。
当一个服务响应过慢的时候,咱们立刻能关联出来是否在作Java GC,或是其所在的计算结点上是否有资源不足的状况,或是依赖的服务是否出现了问题。
当发现一个SQL操做过慢的时候,咱们能立刻知道其会影响哪一个对外服务的API。
当发现一个消息队列拥塞的时候,咱们能立刻知道其会影响哪些对外服务的API。
总之,咱们就是想知道用户访问哪些请求会出现问题,这对于咱们了解故障的影响面很是有帮助。
一旦了解了这些信息,咱们就能够作出调度。好比:
一旦发现某个服务过慢是由于CPU使用过多,咱们就能够作弹性伸缩。
一旦发现某个服务过慢是由于MySQL出现了一个慢查询,咱们就没法在应用层上作弹性伸缩,只能作流量限制,或是降级操做了。
因此,一个分布式系统,或是一个自动化运维系统,或是一个Cloud Native的云化系统,最重要的事就是把监控系统作好。在把数据收集好的同时,更重要的是把数据关联好。这样,咱们才可能很快地定位故障,进而才能进行自动化调度。
上图只是简单地展现了一个分布式系统的服务调用连接上都在报错,其根本缘由是数据库连接过多,服务不过来。另一个缘由是,Java在作Full GC致使处理过慢。因而,消息队列出现消息堆积堵塞。这个图只是一个示例,其形象地体现了在分布式系统中监控数据关联的重要性。
回顾一下今天的要点内容。首先,我强调了全栈系统监控的重要性,它就像是咱们的眼睛,没有它,咱们根本就不知道系统到底发生了什么。随后,从基础层、中间层和应用层三个层面,讲述了全栈监控系统要监控哪些内容。而后,阐释了什么才是好的监控系统,以及如何作出好的监控。最后,欢迎你分享一下你在监控系统中的比较好的实践和方法。
服务治理,你应该听得不少了。可是我想说,你所听到的服务治理可能混合了流量调度等其它内容。咱们这里会把服务治理和流量调度分开来说。因此,这里只涉及服务治理上的一些关键技术,主要有如下几点。
下面,咱们先看看服务关键程度和服务的依赖关系。关于服务关键程度,主要是要咱们梳理和定义服务的重要程度。这不是使用技术能够完成的,这须要细致地管理对业务的理解,才能定义出架构中各个服务的重要程度。
而后,咱们还要梳理出服务间的依赖关系,这点也很是重要。咱们常说,“没有依赖,就没有伤害”。这句话的意思就是说,服务间的依赖是一件很易碎的事。依赖越多,依赖越复杂,咱们的系统就越易碎。
由于依赖关系就像“铁锁连环”同样,一个服务的问题很容易出现一条链上的问题。所以,传统的SOA但愿经过ESB来解决服务间的依赖关系,这也是为何微服务中但愿服务间是没有依赖的,而让上层或是前端业务来整合这些个后台服务。
可是要真正作到服务无依赖,我认为仍是比较有困难的,老是会有一些公有服务会被依赖。咱们只能是下降服务依赖的深度和广度,从而让管理更为简单和简洁。在这一点上,以Spring boot为首的微服务开发框架给开了一个好头。
微服务是服务依赖最优解的上限,而服务依赖的下限是千万不要有依赖环。若是系统架构中有服务依赖环,那么代表你的架构设计是错误的。循环依赖有不少的反作用,最大的问题是这是一种极强的耦合,会致使服务部署至关复杂和难解,并且会致使无穷尽的递归故障和一些你意想不到的的问题。
解决服务依赖环的方案通常是,依赖倒置的设计模式。在分布式架构上,你能够使用一个第三方的服务来解决这个事。好比,经过订阅或发布消息到一个消息中间件,或是把其中的依赖关系抽到一个第三方的服务中,而后由这个第三方的服务来调用这些本来循环依赖的服务。
服务的依赖关系是能够经过技术的手段来发现的,这其中,Zipkin是一个很不错的服务调用跟踪系统,它是经过 Google Dapper这篇论文来实现的。这个工具能够帮你梳理服务的依赖关系,以及了解各个服务的性能。
在梳理完服务的重要程度和服务依赖关系以后,咱们就至关于知道了整个架构的全局。就好像咱们获得了一张城市地图,在这张地图上能够看到城市的关键设施,以及城市的主干道。再加上相关的监控,咱们就能够看到城市各条道路上的工做和拥堵状况。这对于咱们整个分布式架构是很是很是关键的。
我给不少公司作过相关的咨询。当他们须要我帮忙解决一些高并发或是架构问题的时候,我通常都会向他们要一张这样的“地图”,可是几乎全部的公司都没有这样的地图。
有了上面这张地图后,咱们还须要有一个服务发现的中间件,这个中间件是很是很是关键的。由于这个“架构城市”是很是动态的,有的服务会新加进来,有的会离开,有的会增长更多的实例,有的会减小,有的服务在维护过程当中(发布、伸缩等),因此咱们须要有一个服务注册中心,来知道这么几个事。
这个服务注册中心有点像咱们系统运维同窗说的CMDB这样的东西,它也是很是之关键的,由于没有这个东西,咱们将没法知道这些服务运做的状态和状况。
有了这些服务的状态和运行状况以后,你就须要对这些服务的生命周期进行管理了。服务的生命周期一般会有如下几个状态:
这几个状态须要管理好,否则的话,你将不知道这些服务在什么样的状态下。不知道在什么样的状态下,你对整个分布式架构也就没法控制了。
有了这些服务的状态和生命周期的管理,以及服务的重要程度和服务的依赖关系,再加上一个服务运行状态的拟合控制(后面会提到),你一会儿就有了管理整个分布式服务的手段了。一个纷乱无比的世界就能够干干净净地管理起来了。
对于整个架构的版本管理这个事,我只见到亚马逊有这个东西,叫VersionSet,也就是由一堆服务的版本集所造成的整个架构的版本控制。
除了各个项目的版本管理以外,还须要在上面再盖一层版本管理。若是Build过Linux分发包,那么你就会知道,Linux分发包中各个软件的版本上会再盖一层版本控制。毕竟,这些分发包也是有版本依赖的,这样能够解决各个包的版本兼容性问题。
因此,在分布式架构中,咱们也须要一个架构的版本,用来控制其中各个服务的版本兼容。好比,A服务的1.2版本只能和B服务的2.2版本一块儿工做,A服务的上个版本1.1只能和B服务的2.0一块儿工做。这就是版本兼容性。
若是架构中有这样的问题,那么咱们就须要一个上层架构的版本管理。这样,若是咱们要回滚一个服务的版本,就能够把与之有版本依赖的服务也一块儿回滚掉。
固然,通常来讲,在设计过程当中,咱们但愿没有版本的依赖性问题。但可能有些时候,咱们会有这样的问题,那么就须要在架构版本中记录下这个事,以即可以回滚到上一次相互兼容的版本。
要作到这个事,你须要一个架构的manifest,一个服务清单,这个服务清单定义了全部服务的版本运行环境,其中包括但不限于:
每一次对这个清单的变动都须要被记录下来,算是一个架构的版本管理。而咱们上面所说的那个集群控制系统须要可以解读并执行这个清单中的变动,以操做和管理整个集群中的相关变动。
服务和资源的调度有点像操做系统。操做系统一方面把用户进程在硬件资源上进行调度,另外一方面提供进程间的通讯方式,可让不一样的进程在一块儿协同工做。服务和资源调度的过程,与操做系统调度进程的方式很类似,主要有如下一些关键技术。
所谓服务状态不是服务中的数据状态,而是服务的运行状态。也就是服务的Status,而不是State。也就是上述服务运行时生命周期中的状态——Provision,Ready,Run,Scale,Rollback,Update,Destroy,Failed……
服务运行时的状态是很是关键的。服务运行过程当中,状态也是会有变化的,这样的变化有两种。
一种是不预期的变化。好比,服务运行由于故障致使一些服务挂掉,或是别的什么缘由出现了服务不健康的状态。而一个好的集群管理控制器应该可以强行维护服务的状态。在健康的实例数变少时,控制器会把不健康的服务给摘除,而又启动几个新的,强行维护健康的服务实例数。
另一种是预期的变化。好比,咱们须要发布新版本,须要伸缩,须要回滚。这时,集群管理控制器就应该把集群从现有状态迁移到另外一个新的状态。这个过程并非一蹴而就的,集群控制器须要一步一步地向集群发送若干控制命令。这个过程叫“拟合”——从一个状态拟合到另外一个状态,并且要穷尽全部的可能,玩命地不断地拟合,直到达到目的。
详细说明一下,对于分布式系统的服务管理来讲,当须要把一个状态变成另外一个状态时,咱们须要对集群进行一系列的操做。好比,当须要对集群进行Scale的时候,咱们须要:
能够看到,这是一个比较稳健和严谨的Scale过程,这须要集群控制器往生产集群中进行若干次操做。
这个操做的过程必定是比较“慢”的。一方面,须要对其它操做排它;另外一方面,在整个过程当中,咱们的控制系统须要努力地逼近最终状态,直到彻底达到。此外,正在运行的服务可能也会出现问题,离开了咱们想要的状态,而控制系统检测到后,会强行地维持服务的状态。
咱们把这个过程就叫作“拟合”。基本上来讲,集群控制系统都是要干这个事的。没有这种设计的控制系统都不能算作设计精良的控制系统,并且在运行时必定会有不少的坑和bug。
若是研究过Kubernetes这个调度控制系统,你就会看到它的思路就是这个样子的。
有了上述的服务状态拟合的基础工做以后,咱们就能很容易地管理服务的生命周期了,甚至能够经过底层的支持进行便利的服务弹性伸缩和故障迁移。
对于弹性伸缩,在上面我已经给出了一个服务伸缩所须要的操做步骤。仍是比较复杂的,其中涉及到了:
而对于故障迁移,也就是服务的某个实例出现问题时,咱们须要自动地恢复它。对于服务来讲,有两种模式,一种是宠物模式,一种是奶牛模式。
对于这两种模式,在运行中也是比较复杂的,其中涉及到了:
咱们能够看到,弹性伸缩和故障恢复须要很类似的技术步骤。可是,要完成这些事情并不容易,你须要作不少工做,并且有不少细节上的问题会让你感到焦头烂额。
固然,好消息是,咱们很是幸运地生活在了一个比较不错的时代,由于有Docker和Kubernetes这样的技术,能够很是容易地让咱们作这个工做。
可是,须要把传统的服务迁移到Docker和Kubernetes上来,再加上更上层的对服务生命周期的控制系统的调度,咱们就能够作到一个彻底自动化的运维架构了。
正如上面和操做系统作的类比同样,一个好的操做系统须要可以经过必定的机制把一堆独立工做的进程给协同起来。在分布式的服务调度中,这个工做叫作Orchestration,国内把这个词翻译成“编排”。
从《分布式系统架构的冰与火》一文中的SOA架构演化图来看,要完成这个编排工做,传统的SOA是经过ESB(Enterprise Service Bus)——企业服务总线来完成的。ESB的主要功能是服务通讯路由、协议转换、服务编制和业务规则应用等。
注意,ESB的服务编制叫Choreography,与咱们说的Orchestration是不同的。
Orchestration的意思是,一个服务像大脑同样来告诉你们应该怎么交互,就跟乐队的指挥同样。(查看Service-oriented Design:A Multi-viewpoint Approach,了解更多信息)。
Choreography的意思是,在各自完成专属本身的工做的基础上,怎样互相协做,就跟芭蕾舞团的舞者同样。
而在微服务中,咱们但愿使用更为轻量的中间件来取代ESB的服务编排功能。
简单来讲,这须要一个API Gateway或一个简单的消息队列来作相应的编排工做。在Spring Cloud中,全部的请求都统一经过API Gateway(Zuul)来访问内部的服务。这个和Kubernetes中的Ingress类似。
我以为,关于服务的编排会直接致使一个服务编排的工做流引擎中间件的产生,这多是由于我受到了亚马逊的软件工程文化的影响所致——亚马逊是一家超级喜欢工做流引擎的公司。经过工做流引擎,能够很是快速地将若干个服务编排起来造成一个业务流程。(你能够看一下AWS上的Simple Workflow服务。)
这就是所谓的Orchestration中的conductor 指挥了。
好了,今天的分享就这些。总结一下今天的主要内容:咱们从服务关键程度、服务依赖关系、整个架构的版本管理等多个方面,全面阐述了分布式系统架构五大关键技术之一——服务资源调度。但愿这些内容能对你有所启发。
关于流量调度,如今不少架构师都把这个事和服务治理混为一谈了。我以为仍是应该分开的。一方面,服务治理是内部系统的事,而流量调度能够是内部的,更是外部接入层的事。另外一方面,服务治理是数据中心的事,而流量调度要作得好,应该是数据中心以外的事,也就是咱们常说的边缘计算,是应该在相似于CDN上完成的事。
因此,流量调度和服务治理是在不一样层面上的,不该该混在一块儿,因此在系统架构上应该把它们分开。
对于一个流量调度系统来讲,其应该具备的主要功能是:
依据系统运行的状况,自动地进行流量调度,在无需人工干预的状况下,提高整个系统的稳定性;
让系统应对爆品等突发事件时,在弹性计算扩缩容的较长时间窗口内或底层资源消耗殆尽的状况下,保护系统平稳运行。
这仍是为了提升系统架构的稳定性和高可用性。
此外,这个流量调度系统还能够完成如下几方面的事情。
全部的这些都应该是一个API Gateway应该作的事。
可是,做为一个API Gateway来讲,由于要调度流量,首先须要扛住流量,并且还须要有一些比较轻量的业务逻辑,因此一个好的API Gateway须要具有如下的关键技术。
高性能。API Gateway必须使用高性能的技术,因此,也就须要使用高性能的语言。
扛流量。要能扛流量,就须要使用集群技术。集群技术的关键点是在集群内的各个结点中共享数据。这就须要使用像Paxos、Raft、Gossip这样的通信协议。由于Gateway须要部署在广域网上,因此还须要集群的分组技术。
业务逻辑。API Gateway须要有简单的业务逻辑,因此,最好是像AWS的Lambda 服务同样,可让人注入不一样语言的简单业务逻辑。
服务化。一个好的API Gateway须要可以经过Admin API来不停机地管理配置变动的,而不是经过一个.conf文件来人肉地修改配置。
基于上述的这几个技术要求,就其本质来讲,目前能够作成这样的API Gateway几乎没有。这也是为何我如今本身开发一个的缘由。你能够到个人官网MegaEase.com上查看相关的产品和技术信息。
对于服务调度来讲,最难办的就是有状态的服务了。这里的状态是State,也就是说,有些服务会保存一些数据,而这些数据是不能丢失的,因此,这些数据是须要随服务一块儿调度的。
通常来讲,咱们会经过“转移问题”的方法来让服务变成“无状态的服务”。也就是说,会把这些有状态的东西存储到第三方服务上,好比Redis、MySQL、ZooKeeper,或是NFS、Ceph的文件系统中。
这些“转移问题”的方式把问题转移到了第三方服务上,因而本身的Java或PHP服务中没有状态,可是Redis和MySQL上则有了状态。因此,咱们能够看到,如今的分布式系统架构中出问题的基本都是这些存储状态的服务。
由于数据存储结点在Scale上比较困难,因此成了一个单点的瓶颈。
要解决数据结点的Scale问题,也就是让数据服务能够像无状态的服务同样在不一样的机器上进行调度,就会涉及数据的replication问题。而数据replication则会带来数据一致性的问题,进而对性能带来严重的影响。
要解决数据不丢的问题,只能经过数据冗余的方法,就算是数据分区,每一个区也须要进行数据冗余处理。这就是数据副本。当出现某个节点的数据丢失时,能够从副本读到。数据副本是分布式系统解决数据丢失异常的惟一手段。简单来讲:
在解决数据副本间的一致性问题时,咱们有一些技术方案。
你能够仔细地读一下我在3年前写的《分布式系统的事务处理》这篇文章。其中我引用了Google App Engine联合创始人赖安·巴里特(Ryan Barrett)在2009年Google I/O上的演讲Transaction Across DataCenter视频 中的一张图。
从上面这张经典的图中,咱们能够看到各类不一样方案的对比。
如今,不少公司的分布式系统事务基本上都是两阶段提交的变种。好比:阿里推出的TCC–Try–Confirm–Cancel,或是我在亚马逊见到的Plan–Reserve–Confirm的方式,等等。凡是经过业务补偿,或是在业务应用层上作的分布式事务的玩法,基本上都是两阶段提交,或是两阶段提交的变种。
换句话说,迄今为止,在应用层上解决事务问题,只有“两阶段提交”这样的方式,而在数据层解决事务问题,Paxos算法则是不二之选。
真正完整解决数据Scale问题的应该仍是数据结点自身。只有数据结点自身解决了这个问题,才能作到对上层业务层的透明,业务层能够像操做单机数据库同样来操做分布式数据库,这样才能作到整个分布式服务架构的调度。
也就是说,这个问题应该解决在数据存储方。可是由于数据存储结果有太多不一样的Scheme,因此如今的数据存储也是多种多样的,有文件系统,有对象型的,有Key-Value式,有时序的,有搜索型的,有关系型的……
这就是为何分布式数据存储系统比较难作,由于很难作出来一个放之四海皆准的方案。类比一下编程中的各类不一样的数据结构你就会明白为何会有这么多的数据存储方案了。
可是咱们能够看到,这个“数据存储的动物园”中,基本上都在解决数据副本、数据一致性和分布式事务的问题。
好比:AWS的Aurora,就是改写了MySQL的InnoDB引擎。为了承诺高可用的SLA,须要写6个副本。其不像国内的MySQL的经过bin log的数据复制,而是更为“惊艳”地复制SQL语句,而后拼命地使用各类tricky的方式来下降latency。好比,使用多线程并行、使用SQL操做的merge等。
MySQL官方也有MySQL Cluster的技术方案。此外,MongoDB、国内的PingCAP的TiDB、国外的CockroachDB,还有阿里的OceanBase都是为了解决大规模数据的写入和读取的问题而出现的数据库软件。因此,我以为成熟的能够用到生产线上的分布式数据库这个事估计也不远了。
而对于一些须要文件存储的,则须要分布式文件系统的支持。试想,一个Kafka或ZooKeeper须要把它们的数据存储到文件系统上。当这个结点有问题时,咱们须要再启动一个Kafka或ZooKeeper的实例,那么也须要把它们持久化的数据搬迁到另外一台机器上。
(注意,虽然Kafka和ZooKeeper是HA的,数据会在不一样的结点中进行复制,可是咱们也应该搬迁数据,这样有利用于新结点的快速启动。不然,新的结点须要等待数据同步,这个时间会比较长,可能会致使数据层的其它问题。)
因而,咱们就须要一个底层是分布式的文件系统,这样新的结点只须要作一个简单的远程文件系统的mount就能够把数据调度到另一台机器上了。
因此,真正解决数据结点调度的方案应该是底层的数据结点。在它们上面作这个事才是真正有效和优雅的。而像阿里的用于分库分表的数据库中间件TDDL或是别的公司叫什么DAL 之类的这样的中间件都会成为过渡技术。
咱们对状态数据调度作个小小的总结。
对于应用层上的分布式事务一致性,只有两阶段提交这样的方式。
而底层存储能够解决这个问题的方式是经过一些像Paxos、Raft或是NWR这样的算法和模型来解决。
状态数据调度应该是由分布式存储系统来解决的,这样会更为完美。可是由于数据存储的Scheme太多,因此,致使咱们有各式各样的分布式存储系统,有文件对象的,有关系型数据库的,有NoSQL的,有时序数据的,有搜索数据的,有队列的……
总之,我相信状态数据调度应该是在IaaS层的数据存储解决的问题,而不是在PaaS层或者SaaS层来解决的。
在IaaS层上解决这个问题,通常来讲有三种方案,一种是使用比较廉价的开源产品,如:NFS、Ceph、TiDB、CockroachDB、ElasticSearch、InfluxDB、MySQL Cluster和Redis Cluster之类的;另外一种是用云计算厂商的方案。固然,若是不差钱的话,能够使用更为昂贵的商业网络存储方案。
回顾一下今天分享的主要内容。首先,我先明确表态,不要将流量调度和服务治理混为一谈(固然,服务治理是流量调度的前提),并比较了二者有何不一样。而后,讲述了流量调度的主要功能和关键技术。接着进入本文的第二个话题——状态数据调度,讲述了真正完整解决数据Scale问题的应该仍是数据结点自身,并给出了相应的技术方案,随后对状态数据调度进行了小结。
欢迎你也谈一谈经历过的技术场景中是采用了哪些流量和数据调度的技术和产品,遇到过什么样的问题,是怎样解决的?
下篇文章中,咱们将开启一个全新的话题——洞悉PaaS平台的本质。
在了解了前面几篇文章中提的这些问题之后,咱们须要思考一下该怎样解决这些问题。为了解决这些问题,请先让我来谈谈软件工程的本质。
我认为,一家商业公司的软件工程能力主要体如今三个地方。
第一,提升服务的SLA。
所谓服务的SLA,也就是咱们能提供多少个9的系统可用性,而每提升一个9的可用性都是对整个系统架构的从新洗礼。而提升系统的SLA主要表如今两个方面:
你能够看一下我在CoolShell上写的《关于高可用系统》,这篇文章主要讲了构建高可用的系统须要使用分布式系统设计思路。然而这还不够,还须要一个高度自动化的运维和管理系统,由于故障是常态,若是没有自动化的故障恢复,很难提升服务的SLA。
第二,能力和资源重用或复用。
软件工程还有一个重要的能力是让能力和资源能够重用。其主要表如今以下两个方面:
为此,须要咱们有两个重要的能力:一个是“软件抽象的能力”,另外一个是“软件标准化的能力”。你能够认为软件抽象就是找出通用的软件模块或服务,软件标准化就是使用统一的软件通信协议、统一的开发和运维管理方法……这样能让总体软件开发运维的能力和资源获得最大程度的复用,从而增长效率。
第三,过程的自动化。
编程原本就是把一个重复的工做自动化的过程,因此,软件工程的第三个本质就是把软件生产和运维的过程自动化起来。也就是下面这两个方面:
为此,咱们除了须要CI/CD的DevOps式的自动化,也须要可以对正在运行的生产环境中的软件进行自动化运维。
经过了解软件工程的这三个本质,你会发现,咱们上面所说的那些分布式的技术点是高度一致的,也就是下面这三个方面的能力。(是的,世界就是这样的。当参透了本质以后,你会发现世界是大同的。)
只有作到了这些,咱们才可以真正拥有云计算的威力。这就是所谓的Cloud Native。而这些目标都完美地体如今PaaS平台上。
前面讲述的分布式系统关键技术和软件工程的本质,均可以在PaaS平台上获得彻底体现。因此,须要一个PaaS平台把那么多的东西给串联起来。这里,我结合本身的认知给你讲一下PaaS相关的东西,并把前面讲过的全部东西作一个总结。
一个好的PaaS平台应该具备分布式、服务化、自动化部署、高可用、敏捷以及分层开放的特征,并可与IaaS实现良好的联动。
下面这三件事是PaaS跟传统中间件最大的差异。
从下面的图中能够看到,我用了Docker+Kubernetes层来作了一个“技术缓冲层”。也就是说,若是没有Docker和Kubernetes,构建PaaS将会复杂不少。固然,若是你正在开发一个相似PaaS的平台,那么你会发现本身开发出来的东西会跟Docker和Kubernetes很是像。相信我,最终你仍是会放弃本身的轮子而采用Docker+Kubernetes的。
在Docker+Kubernetes层之上,咱们看到了两个相关的PaaS层。一个是PaaS调度层,不少人将其称为iPaaS;另外一个是PaaS能力层,一般被称为aPaaS。没有PaaS调度层,PaaS能力层很难被管理和运维,而没有PaaS能力层,PaaS就失去了提供实际能力的业务价值。而本文更多的是在讲PaaS调度层上的东西。
在两个相关的PaaS层之上,有一个流量调度的接入模块,这也是PaaS中很是关键的东西。流控、路由、降级、灰度、聚合、串联等等都在这里,包括最新的AWS Lambda Service的小函数等也能够放在这里。这个模块应该是像CDN那样来部署的。
而后,在这个图的两边分别是与运营和运维相关的。运营这边主要是管理一些软件资源方面的东西(像DockerHub和CMDB的东西),以及外部接入和开放平台上的东西,这主要是对外提供能力的相关组件;而运维这边主要是对内的相关东西,主要就是DevOps的这套东西。
总结一下,一个完整的PaaS平台会包括如下几部分。
由于我画的是一个大而全的东西,因此看上去彷佛很重很复杂。实际上,其中的不少东西是能够根据本身的需求被简化和裁剪的,并且不少开源软件能帮你简化好多工做。虽然构建PaaS平台看上去很麻烦,可是其实并非很复杂,不要被我吓到了。哈哈。
下面的图给出了一个大概的软件生产、运维和服务接入,它把以前的东西都串起来了。
从左上开始软件构建,进入软件资产库(Docker Registry+一些软件的定义),而后走DevOps的流程,经过总体架构控制器进入生产环境,生产环境经过控制器操做Docker+Kubernetes集群进行软件部署和生产变动。
其中,同步服务的运行状态,并经过生命周期管理来拟合状态,如图右侧部分所示。服务运行时的数据会进入到相关应用监控,应用监控中的一些监控事件会同步到生命周期管理中,再由生命周期管理器来作出决定,经过控制器来调度服务运行。当应用监控中心发现流量变化,要进行强制性伸缩时,它经过生命周期管理来通知控制系统进行伸缩。
左下是服务接入的相关组件,主要是网关服务,以及API聚合编排和流程处理。这对应于以前说过的流量调度和API Gateway的相关功能。
恭喜你,已经听完了《分布式系统架构的本质》系列文章的7篇文章。下面,咱们对这些内容作一下总结。
传统的单体架构系统容量显然是有上限的。同时,为了应对有计划和无计划的下线时间,系统的可用性也是有其极限的。分布式系统为以上两个问题提供了解决方案,而且还附带有其余优点。可是,要同时解决这两个问题决非易事。为了构建分布式系统,咱们面临的主要问题以下。
为了解决这些问题,咱们深刻了解了如下这些解决方案。
你已经看到,解决分布式服务的吞吐量和可用性问题不是件容易的事,以及目前的主流技术是怎么办到的。衍生出来的许多子问题,每个都值得去细化、去研究其解决方案。这已经超出本文的篇幅所能及的了,但的确都是值得咱们作技术的人去深刻思考的。
咱们在以前的系列文章《分布式系统架构的本质》中说过,分布式系统的一个关键技术是“数据调度”。由于咱们须要扩充节点,提升系统的高可用性,因此必需冗余数据结点。创建数据结点的副本看上去容易,但其中最大的难点就是分布式一致性的问题。下面,我会带你看看数据调度世界中的一些技术点以及相关的技术论文。
对于分布式的一致性问题,相信你在前面看过好几回下面这张图。从中,咱们能够看出,Paxos算法的重要程度。还有人说,分布式下真正的一致性算法只有Paxos算法。
Paxos算法,是莱斯利·兰伯特(Lesile Lamport)于1990年提出来的一种基于消息传递且具备高度容错特性的一致性算法。可是这个算法太过于晦涩,因此,一直以来都处于理论上的论文性质的东西。
其进入工程圈的源头在于Google的Chubby lock——一个分布式的锁服务,用在了Bigtable中。直到Google发布了下面的这两篇论文,Paxos才进入到工程界的视野中来。
Google与Big Table相齐名的还有另外两篇论文。
不过,这几篇论文中并无讲太多的Paxos算法细节上的内容,反而在论文Paxos Made Live – An Engineering Perspective 中提到了不少工程实现的细节。好比,Google实现Paxos时遇到的各类问题和解决方案,讲述了从理论到实际应用两者之间巨大的鸿沟。
尤为在满地都是坑的分布式系统领域,这篇论文没有过多讨论Paxos算法自己,而是在讨论如何将理论应用到实践,如何弥补理论在实践中的不足,如何取舍,如何测试,这些在实践中的各类问题才是工程的魅力。因此建议你读一读。
Paxos算法的原版论文我在这里就不贴了,由于一来比较晦涩,二来也不易懂。推荐一篇比较容易读的——Neat Algorithms - Paxos ,这篇文章中还有一些小动画帮助你读懂。还有一篇能够帮你理解的文章是Paxos by Examples。
若是你要本身实现Paxos算法,这里有几篇文章供你参考。
Paxos Made Code ,做者是马克罗·普里米(Macro Primi),他实现了一个Paxos开源库libpaxos。
Paxos for System Builders ,以一个系统实现者的角度讨论了实现Paxos的诸多具体问题,好比Leader选举、数据及消息类型、流控等。
Paxos Made Moderately Complex,这篇文章比较新,是2011年才发表的。文中介绍了不少实现细节,并提供了不少伪代码,一方面能够帮助理解Paxos,另外一方面也能够据此实现一个Paxos。
Paxos Made Practical主要介绍如何采用Paxos实现replication。
除了马克罗·普里米的那个开源实现外,到GitHub上找一下,你就会看到这些项目:Plain Paxos Implementations Python & Java、A go implementation of the Paxos algorithm 。
ZooKeeper 有和Paxos很是类似的一些特征,好比,领导选举、提案号等,可是它本质上不是Paxos协议,而是本身发明的Zab协议,有兴趣的话,能够读一下这篇论文:Zab: High-Performance broadcast for primary-backup systems。
上述的Google File System、MapReduce、Bigtable并称为“谷三篇”。基本上来讲,整个世界工程系统由于这三篇文章,开始向分布式系统演化,而云计算中的不少关键技术也是由于这三篇文章才得以成熟。 后来,雅虎公司也基于这三篇论文开发了一个开源的软件——Hadoop。
由于Paxos算法太过于晦涩,并且在实际的实现上有太多的坑,并不太容易写对。因此,有人搞出了另一个一致性的算法,叫Raft。其原始论文是 In search of an Understandable Consensus Algorithm (Extended Version) 寻找一种易于理解的Raft算法。这篇论文的译文在InfoQ上《Raft一致性算法论文译文》,推荐你读一读。
Raft算法和Paxos的性能和功能是同样的,可是它和Paxos算法的结构不同,这使Raft算法更容易理解而且更容易实现。那么Raft是怎样作到的呢?
Raft把这个一致性的算法分解成了几个部分,一个是领导选举(Leader Selection),一个是日志复制(Log Replication),一个是安全性(Safety),还有一个是成员变化(Membership Changes)。对于通常人来讲,Raft协议比Paxos的学习曲线更低,也更平滑。
Raft协议中有一个状态机,每一个结点会有三个状态,分别是 Leader、Candidate和Follower。Follower只响应其余服务器的请求,若是没有收到任何信息,它就会成为一个Candidate,并开始进行选举。收到大多数人赞成选票的人会成为新的Leader。
一旦选举出了一个Leader,它就开始负责服务客户端的请求。每一个客户端的请求都包含一个要被复制状态机执行的指令。Leader首先要把这个指令追加到log中造成一个新的entry,而后经过AppendEntries RPC并行地把该entry发给其余服务器(server)。若是其余服务器没发现问题,复制成功后会给Leader一个表示成功的ACK。
Leader收到大多数ACK后应用该日志,返回客户端执行结果。若是Follower崩溃 (crash)或者丢包,Leader会不断重试AppendEntries RPC。
这里推荐几个不错的Raft算法的动画演示。
后面,业内又搞出来一些工程上的东西,好比Amazon的DynamoDB,其论文Dynamo: Amazon's Highly Available Key Value Store 的影响力很是大。这篇论文中讲述了Amazon 的DynamoDB是如何知足系统的高可用、高扩展和高可靠的。其中展现了系统架构是如何作到数据分布以及数据一致性的。
GFS采用的是查表式的数据分布,而DynamoDB采用的是计算式的,也是一个改进版的经过虚拟结点减小增长结点带来数据迁移的一致性哈希。另外,这篇论文中还讲述了一个NRW模式用于让用户能够灵活地在CAP系统中选取其中两项,这使用到了Vector Clock——向量时钟来检测相应的数据冲突。最后还介绍了使用Handoff的机制对可用性的提高。
这篇文章中有几个关键的概念,一个是Vector Clock,另外一个是Gossip协议。
提到向量时钟就须要提一下逻辑时钟。所谓逻辑时间,也就是在分布系统中为了解决消息有序的问题,因为在不一样的机器上有不一样的本地时间,这些本地时间的同步很难搞,会致使消息乱序。
因而Paxos算法的发明人兰伯特(Lamport)搞了个向量时钟,每一个系统维护一个本地的计数器,这就是所谓的逻辑时钟。每执行一个事件(例如向网络发送消息,或是交付到应用层)都对这个计数器作加1操做。当跨系统的时候,在消息体上附着本地计算器,当接收端收到消息时,更新本身的计数器(取对端传来的计数器和本身当成计数器的最大值),也就是调整本身的时钟。
逻辑时钟能够保证,若是事件A先于事件B,那么事件A的时钟必定小于事件B的时钟,可是返过来则没法保证,由于返过来没有因果关系。因此,向量时钟解释了因果关系。向量时钟维护了数据更新的一组版本号(版本号其实就是使用逻辑时钟)。
假如一个数据须要存在三个结点上A、B、C。那么向量维度就是3,在初始化的时候,全部结点对于这个数据的向量版本是[A:0, B:0, C:0]。当有数据更新时,好比从A结点更新,那么,数据的向量版本变成[A:1, B:0, C:0],而后向其余结点复制这个版本,其在语义上表示为我当前的数据是由A结果更新的,而在逻辑上则可让分布式系统中的数据更新的顺序找到相关的因果关系。
这其中的逻辑关系,你能够看一下 马萨诸塞大学课程 Distributed Operating System 中第10节 Clock Synchronization 这篇讲议。关于Vector Clock,你能够看一下Why Vector Clocks are Easy和Why Vector Clocks are Hard 这两篇文章。
另外,DynamoDB中使用到了Gossip协议来作数据同步,这个协议的原始论文是 Efficient Reconciliation and Flow Control for Anti-Entropy Protocols。Gossip算法也是Cassandra使用的数据复制协议。这个协议就像八卦和谣言传播同样,能够 “一传10、十传百”传播开来。可是这个协议看似简单,细节上却很是麻烦。
根据这篇论文,节点之间存在三种通讯方式。
push方式。A节点将数据(key,value,version)及对应的版本号推送给B节点,B节点更新A中比本身新的数据。
pull 方式。A仅将数据key,version推送给B,B将本地比A新的数据(key,value,version)推送给A,A更新本地。
push/pull方式。与pull相似,只是多了一步,A再将本地比B新的数据推送给B,B更新本地。
若是把两个节点数据同步一次定义为一个周期,那么在一个周期内,push需通讯1次,pull需2次,push/pull则需3次。从效果上来说,push/pull最好,理论上一个周期内能够使两个节点彻底一致。直观感受上,也是push/pull的收敛速度最快。
另外,每一个节点上的又须要一个协调机制,也就是如何交换数据能达到最快的一致性——消除节点的不一致性。上面所讲的push、pull等是通讯方式,协调是在通讯方式下的数据交换机制。
协调所面临的最大问题是,一方面须要找到一个经济的方式,由于不可能每次都把一个节点上的数据发送给另外一个节点;另外一方面,还须要考虑到相关的容错方式,也就是当由于网络问题不可达的时候,怎么办?
通常来讲,有两种机制:一种是以固定几率传播的Anti-Entropy机制,另外一种是仅传播新到达数据的Rumor-Mongering机制。前者有完备的容错性,可是须要更多的网络和CPU资源,后者则反过来,不耗资源,但在容错性上难以保证。
Anti-Entropy的机制又分为Precise Reconciliation(精确协调)和Scuttlebutt Reconciliation(总体协调)这两种。前者但愿在每次通讯周期内都很是精确地消除双方的不一致性,具体表现就是互发对方须要更新的数据。由于每一个结点均可以读写,因此这须要每一个数据都要独立维护本身的版本号。
而总体协调与精确协调不一样的是,总体协调不是为每一个数据都维护单独的版本号,而是每一个节点上的数据统一维护一个版本号,也就是一个一致的全局版本。这样与其余结果交换数据的时候,就只须要比较节点版本,而不是数据个体的版本,这样会比较经济一些。若是版本不同,则须要作精确协调。
由于篇幅问题,这里就很少说了,你能够看看原始的论文,还能够去看看Cassandra中的源码,以及到GitHub搜一下其余人的实现。多说一句,Cassandra的实现是基于总体协调的push/pull模式。
关于Gossip的一些图示化的东西,你能够看一下动画gossip visualization。
上面讲的都是一些基本概念相关的东西,下面咱们来谈谈数据库方面的一些论文。
一篇是AWS Aurora的论文 Amazon Aurora: Design Considerations for High Throughput Cloud –Native Relation Databases。
Aurora是AWS将MySQL的计算和存储分离后,计算节点scale up,存储节点scale out。并把其redo log独立设计成一个存储服务,把分布式的数据方面的东西所有甩给了底层存储系统。从而提升了总体的吞吐量和水平的扩展能力。
Aurora要写6份拷贝,可是其只须要把一个Quorum中的日志写成功就能够了。以下所示。能够看到,将存储服务作成一个跨数据中心的服务,提升数据库容灾,下降性能影响。
对于存储服务的设计,核心的原理就是latency必定要低,毕竟写6个copy是一件开销很大的事。因此,基本上来讲,Aurora用的是异步模型,而后拼命地作并行处理,其中用到的也是Gossip协议。以下所示。
在上面这个图中,咱们能够看到,完成前两步,就能够ACK回调用方。也就是说,只要数据在本地落地了,就能够返回成功了。而后,对于六个副本,这个log会同时发送到6个存储结点,只须要有大于4个成功ACK,就算写成功了。第4步咱们能够看到用的是Gossip协议。而后,第5步产生cache 页,便于查询。第6步在S3作Snapshot,相似于Checkpoint。
第二篇比较有表明的论文是Google的 Spanner: Google’s Globally-Distributed Database。 Spanner 是Google的全球分布式数据库Globally-Distributed Database) 。Spanner的扩展性达到了使人咋舌的全球级,能够扩展到数百万台机器,数以百计的数据中心,上万亿的行。更给力的是,除了夸张的扩展性以外,它还能同时经过同步复制和多版原本知足外部一致性,可用性也是很好的。
下面是Spanserver的一个架构。
咱们能够看到,每一个数据中心都会有一套Colossus,这是第二代的GFS。每一个机器有100-1000个tablet,也就是至关数据库表中的行集,物理存储就是数据文件。好比,一张表有2000行,而后有20个tablet,那么每一个tablet分别有100行数据。
在tablet上层经过Paxos协议进行分布式跨数据中心的一致性数据同步。Paxos会选出一个replica作Leader,这个Leader的寿命默认是10s,10s后重选。Leader就至关于复制数据的master,其余replica的数据都是从它那里复制的。读请求能够走任意的replica,可是写请求只有去Leader。这些replica统称为一个Paxos Group。
Group之间也有数据交互传输,Google定义了最小传输复制单元directory,是一些有共同前缀的key记录,这些key也有相同的replica配置属性。
目前,基于Spanner论文的开源实现有两个,一个是Google公司本身的人出来作的CockroachDB,另外一个是国人作的TiDB。
正如我在以前的分布式系统的本质文章里所说到的,分布式的服务的调度须要一个分布式的存储系统来支持服务的数据调度。而咱们能够看到,各大公司都在分布式的数据库上作各类各样的创新,他们都在使用底层的分布式文件系统来作存储引擎,把存储和计算分离开来,而后使用分布式一致性的数据同步协议的算法来在上层提供高可用、高扩展的支持。
从这点来看,能够预见到,过去的分库分表并经过一个数据访问的代理服务的玩法,应该在不久就会过期就会成为历史。真正的现代化的分布式数据存储就是Aurora和Spanner这样的方式。
经过上面的这些论文和相关的工程实践以及开源项目,相信可让你在细节方面对分布式中最难的一块——数据调度方面有更多的认识。
前段时间,我写了一系列分布式系统架构方面的文章,有不少读者纷纷留言讨论相关的话题,还有读者留言表示对分布式系统架构这个主题感兴趣,但愿我能推荐一些学习资料。
就像我在前面的文章中屡次提到的,分布式系统的技术栈巨大无比,因此我要推荐的学习资料也比较多,会在后面的文章中陆续发出。在今天这篇文章中,我将推荐一些分布式系统的基础理论和一些不错的图书和资料。
这篇文章比较长,因此我特地整理了目录,帮你快速找到本身感兴趣的内容。
下面这些基础知识有可能你已经知道了,不过仍是容我把其分享在这里。我但愿用比较通俗易懂的文字将这些枯燥的理论知识讲请楚。
CAP定理是分布式系统设计中最基础,也是最为关键的理论。它指出,分布式数据存储不可能同时知足如下三个条件。
一致性(Consistency):每次读取要么得到最近写入的数据,要么得到一个错误。
可用性(Availability):每次请求都能得到一个(非错误)响应,但不保证返回的是最新写入的数据。
分区容忍(Partition tolerance):尽管任意数量的消息被节点间的网络丢失(或延迟),系统仍继续运行。
也就是说,CAP定理代表,在存在网络分区的状况下,一致性和可用性必须二选一。而在没有发生网络故障时,即分布式系统正常运行时,一致性和可用性是能够同时被知足的。这里须要注意的是,CAP定理中的一致性与ACID数据库事务中的一致性大相径庭。
掌握CAP定理,尤为是可以正确理解C、A、P的含义,对于系统架构来讲很是重要。由于对于分布式系统来讲,网络故障在所不免,如何在出现网络故障的时候,维持系统按照正常的行为逻辑运行就显得尤其重要。你能够结合实际的业务场景和具体需求,来进行权衡。
例如,对于大多数互联网应用来讲(如门户网站),由于机器数量庞大,部署节点分散,网络故障是常态,可用性是必需要保证的,因此只有舍弃一致性来保证服务的AP。而对于银行等,须要确保一致性的场景,一般会权衡CA和CP模型,CA模型网络故障时彻底不可用,CP模型具有部分可用性。
CA (consistency + availability),这样的系统关注一致性和可用性,它须要很是严格的全体一致的协议,好比“两阶段提交”(2PC)。CA系统不能容忍网络错误或节点错误,一旦出现这样的问题,整个系统就会拒绝写请求,由于它并不知道对面的那个结点是否挂掉了,仍是只是网络问题。惟一安全的作法就是把本身变成只读的。
CP (consistency + partition tolerance),这样的系统关注一致性和分区容忍性。它关注的是系统里大多数人的一致性协议,好比:Paxos算法(Quorum类的算法)。这样的系统只须要保证大多数结点数据一致,而少数的结点会在没有同步到最新版本的数据时变成不可用的状态。这样可以提供一部分的可用性。
AP (availability + partition tolerance),这样的系统关心可用性和分区容忍性。所以,这样的系统不能达成一致性,须要给出数据冲突,给出数据冲突就须要维护数据版本。Dynamo就是这样的系统。
然而,仍是有一些人会错误地理解CAP定理,甚至误用。Cloudera工程博客中,CAP Confusion: Problems with ‘partition tolerance’一文中对此有详细的阐述。
在谷歌的Transaction Across DataCenter视频中,咱们能够看到下面这样的图。这个是CAP理论在具体工程中的体现。
本文是英文维基百科上的一篇文章。它是Sun公司的劳伦斯·彼得·多伊奇(Laurence Peter Deutsch)等人于1994~1997年提出的,讲的是刚刚进入分布式计算领域的程序员常会有的一系列错误假设。
多伊奇于1946年出生在美国波士顿。他创办了阿拉丁企业(Aladdin Enterprises),并在该公司编写出了著名的Ghostscript开源软件,于1988年首次发布。
他在学生时代就和艾伦·凯(Alan Kay)等比他年长的人一块儿开发了Smalltalk,而且他的开发成果激发了后来Java语言JIT编译技术的创造灵感。他后来在Sun公司工做并成为Sun的公司院士。在1994年,他成为了ACM院士。
基本上,每一个人刚开始创建一个分布式系统时,都作了如下8条假定。随着时间的推移,每一条都会被证实是错误的,也都会致使严重的问题,以及痛苦的学习体验。
阿尔农·罗特姆-盖尔-奥兹(Arnon Rotem-Gal-Oz)写了一篇长文Fallacies of Distributed Computing Explained来解释这些点。
因为他写这篇文章的时候已是2006年了,因此从中能看到这8条常见错误被提出十多年后还有什么样的影响:一是,为何当今的分布式软件系统也须要避免这些设计错误;二是,在当今的软硬件环境里,这些错误意味着什么。好比,文中在谈“延迟为零”假设时,还谈到了AJAX,而这是2005年开始流行的技术。
而加勒思·威尔逊(Gareth Wilson)的文章则用平常生活中的例子,对这些点作了更为通俗的解释。
这8个须要避免的错误不只对于中间件和底层系统开发者及架构师是重要的知识,并且对于网络应用程序开发者也一样重要。分布式系统的其余部分,如容错、备份、分片、微服务等也许能够对应用程序开发者部分透明,但这8点则是应用程序开发者也必须知道的。
为何咱们要深入地认识这8个错误?是由于,这要咱们清楚地认识到——在分布式系统中错误是不可能避免的,咱们能作的不是避免错误,而是要把错误的处理当成功能写在代码中。
后面,我会写一个系列的文章来谈一谈,分布式系统容错设计中的一些常见设计模式。敬请关注!
本文做者认为,推荐大量的理论论文是学习分布式系统理论的错误方法,除非这是你的博士课程。由于论文一般难度大又很复杂,须要认真学习,并且须要理解这些研究成果产生的时代背景,才能真正的领悟到其中的精妙之处。
在本文中,做者给出了他整理的分布式工程师必需要掌握的知识列表,并直言掌握这些足够设计出新的分布式系统。首先,做者推荐了4份阅读材料,它们共同归纳了构建分布式系统的难点,以及全部工程师必须克服的技术难题。
Distributed Systems for Fun and Profit,这是一本小书,涵盖了分布式系统中的关键问题,包括时间的做用和不一样的复制策略。后文中对这本书有较详细的介绍。
Notes on distributed systems for young bloods,这篇文章中没有理论,是一份适合新手阅读的分布式系统实践笔记。
A Note on Distributed Systems,这是一篇经典的论文,讲述了为何在分布式系统中,远程交互不能像本地对象那样进行。
The fallacies of distributed computing,每一个分布式系统新手都会作的8个错误假设,并探讨了其会带来的影响。上文中专门对这篇文章作了介绍。
随后,分享了几个关键点。
能够参考Lamport时钟和Vector时钟,还能够看看Dynamo论文。
最终一致性以及其余技术方案在以系统行为弱保证为代价,来试图避免这种系统压力。阅读Dynamo论文和帕特·赫尔兰(Pat Helland)的经典论文Life Beyond Transactions能获很得大启发。
基本原语(Basic primitives)。在分布式系统中几乎没有一致认同的基本构建模块,但目前在愈来愈多地在出现。好比Leader选举,能够参考Bully算法;分布式状态机复制,能够参考维基百科和Lampson的论文,后者更权威,只是有些枯燥。
基本结论(Fundamental Results)。某些事实是须要吸取理解的,有几点:若是进程之间可能丢失某些消息,那么不可能在实现一致性存储的同时响应全部的请求,这就是CAP定理;一致性不可能同时知足如下条件:a. 老是正确,b. 在异步系统中只要有一台机器发生故障,系统老是能终止运行——中止失败(FLP不可能性);通常而言,消息交互少于两轮都不可能达成共识(Consensus)。
真实系统(Real systems)。学习分布式系统架构最重要的是,结合一些真实系统的描述,反复思考和点评其背后的设计决策。如谷歌的GFS、Spanner、Chubby、BigTable、Dapper等,以及Dryad、Cassandra和Ceph等非谷歌系统。
FLP不可能性的名称起源于它的三位做者,Fischer、Lynch和Paterson。它是关于理论上能作出的功能最强的共识算法会受到怎样的限制的讨论。
所谓共识问题,就是让网络上的分布式处理者最后都对同一个结果值达成共识。该解决方案对错误有恢复能力,处理者一旦崩溃之后,就再也不参与计算。在同步环境下,每一个操做步骤的时间和网络通讯的延迟都是有限的,要解决共识问题是可能的,方式是:等待一个完整的步长来检测某个处理者是否已失败。若是没有收到回复,那就假定它已经崩溃。
共识问题有几个变种,它们在“强度”方面有所不一样——一般,一个更“强”问题的解决方案同时也能解决比该问题更“弱”的问题。共识问题的一个较强的形式以下。
给出一个处理者的集合,其中每个处理者都有一个初始值:
这三个特性分别被称为“终止”、“一致赞成”和“有效性”。任何一个具有这三点特性的算法都被认为是解决了共识问题。
FLP不可能性则讨论了异步模型下的状况,主要结论有两条。
在异步模型下不存在一个彻底正确的共识算法。不只上述较“强”形式的共识算法不可能实现,FLP还证实了比它弱一些的、只须要有一些无错误的进程作决定就足够的共识算法也是不可能实现的。
在异步模型下存在一个部分正确的共识算法,前提是全部无错误的进程都总能作出一个决定,此外没有进程会在它的执行过程当中死亡,而且初始状况下超过半数进程都是存活状态。
FLP的结论是,在异步模型中,仅一个处理者可能崩溃的状况下,就已经没有分布式算法能解决共识问题。这是该问题的理论上界。其背后的缘由在于,异步模型下对于一个处理者完成工做而后再回复消息所需的时间并无上界。所以,没法判断出一个处理者究竟是崩溃了,仍是在用较长的时间来回复,或者是网络有很大的延迟。
FLP不可能性对咱们还有别的启发。一是网络延迟很重要,网络不能长时间处于拥塞状态,不然共识算法将可能由于网络延迟过长而致使超时失败。二是计算时间也很重要。对于须要计算共识的处理过程(进程),如分布式数据库提交,须要在短期里就计算出可否提交的结果,那就要保证计算结点资源充分,特别是内存容量、磁盘空闲时间和CPU时间方面要足够,并在软件层面确保计算不超时。
另外一个问题是,像Paxos这样的共识算法为何可行?实际上它并不属于FLP不可能性证实中所说的“彻底正确”的算法。它的正确性会受超时值的影响。但这并不妨碍它在实践中有效,由于咱们能够经过避免网络拥塞等手段来保证超时值是合适的。
它是分布式系统基础课的课程提纲,也是一份很棒的分布式系统介绍,几乎涵盖了全部知识点,并辅以简洁并切中要害的说明文字,很是适合初学者提纲挈领地了解知识全貌,快速与现有知识结合,造成知识体系。此外,还能够把它做为分布式系统的知识图谱,根据其中列出的知识点一一搜索,你能学会全部的东西。
这是一本免费的电子书。做者撰写此书的目的是但愿以一种更易于理解的方式,讲述以亚马逊的Dynamo、谷歌的BigTable和MapReduce等为表明的分布式系统背后的核心思想。
于是,书中着力撰写分布式系统中的关键概念,以便让读者可以快速了解最为核心的知识,而且进行了足够详实的讲述,方便读者体会和理解,又不至于陷入细节。
全书分为五章,讲述了扩展性、可用性、性能和容错等基础知识,FLP不可能性和CAP定理,探讨了大量的一致性模型;讨论了时间和顺序,及时钟的各类用法。随后,探讨了复制问题,如何防止差别,以及如何接受差别。此外,每章末尾都给出了针对本章内容的扩展阅读资源列表,这些资料是对本书内容的很好补充。
本书是由计算机科学家安德鲁·斯图尔特·塔能鲍姆(Andrew S. Tanenbaum)和其同事马丁·范·斯蒂恩(Martin van Steen)协力撰写的,是分布式系统方面的经典教材。
语言简洁,内容通俗易懂,介绍了分布式系统的七大核心原理,并给出了大量的例子;系统讲述了分布式系统的概念和技术,包括通讯、进程、命名、同步化、一致性和复制、容错以及安全等;讨论了分布式应用的开发方法(即范型)。
但本书不是一本指导“如何作”的手册,仅适合系统性地学习基础知识,了解编写分布式系统的基本原则和逻辑。中文翻译版为《分布式系统原理与范型》(第二版)。
这是一本免费的在线小册子,其中文翻译版为可扩展的Web架构和分布式系统。
本书主要针对面向的互联网(公网)的分布式系统,但其中的原理或许也能够应用于其余分布式系统的设计中。做者的观点是,经过了解大型网站的分布式架构原理,小型网站的构建也能从中受益。本书从大型互联网系统的常见特性,如高可用、高性能、高可靠、易管理等出发,引出了一个相似于Flickr的典型的大型图片网站的例子。
首先,从程序模块化易组合的角度出发,引出了面向服务架构(SOA)的概念。同时,引伸出写入和读取二者的性能问题,及对此两者如何调度的考量——在当今的软硬件架构上,写入几乎老是比读取更慢,包括软件层面引发的写入慢(如数据库的一致性要求和B树的修改)和硬件层面引发的写入慢(如SSD)。
网络提供商提供的下载带宽也一般比上传带宽更大。读取每每能够异步操做,还能够作gzip压缩。写入则每每须要保持链接直到数据上传完成。所以,每每咱们会想把服务作成读写分离的形式。而后经过一个Flickr的例子,介绍了他们的服务器分片式集群作法。
接下来说了冗余。数据的冗余异地备份(如master-slave)、服务的多版本冗余、避免单点故障等。
随后,在冗余的基础上,讲了多分区扩容,亦即横向扩容。横向扩容是在单机容量没法知足需求的状况下不得不作的设计。但横向扩容会带来一个问题,即数据的局域性会变差。原本数据能够存在于同一台服务器上,但如今数据不得不存在于不一样服务器上,潜在地下降了系统的性能(主要是可能延长响应时间)。另外一个问题是多份数据的不一致性。
以后,本书开始深刻讲解数据访问层面的设计。首先抛出一个大型数据(TB级以上)的存储问题。若是内存都没法缓存该数据量,性能将大幅降低,那么就须要缓存数据。数据能够缓存在每一个节点上。
但若是为全部节点使用负载均衡,那么分配到每一个节点的请求将十分随机,大大下降缓存命中率,从而致使低效的缓存。接下来考虑全局缓存的设计。再接下来考虑分布式缓存的设计。进一步,介绍了Memcached,以及Facebook的缓存设计方案。
代理服务器则能够用于把多个重复请求合并成一个,对于公网上的公共服务来讲,这样作能够大大减小对数据层访问的次数。Squid和Varnish是两个可用于生产的代理服务软件。
当知道所须要读取的数据的元信息时,好比知道一张图片的URL,或者知道一个要全文搜索的单词时,索引就能够帮助找到那几台存有该信息的服务器,并从它们那里获取数据。文中扩展性地讨论了本话题。
接下来谈负载均衡器,以及一些典型的负载均衡拓扑。而后讨论了对于用户会话数据如何处理。好比,对于电子商务网站,用户的购物车在没有下单以前都必须保持有效。
一种办法是让用户会话与服务器产生关联,但这样作会较难实现自动故障转移,如何作好是个问题。另外,什么时候该使用负载均衡是个问题。有时节点数量少的状况下,只要使用轮换式DNS便可。负载均衡也会让在线性能问题的检测变得更麻烦。
对于写入的负载,能够用队列的方式来减小对服务器的压力,保证服务器的效率。消息队列的开源实现有不少,如RabbitMQ、ActiveMQ、BeanstalkD,但有些队列方案也使用了如Zookeeper,甚至是像Redis这样的存储服务。
本书主要讲述了高性能互联网分布式服务的架构方案,并介绍了许多实用的工具。做者指出这是一个使人兴奋的设计领域,虽然只讲了一些皮毛,但这一领域不只如今有不少创新,未来也会愈来愈多。
本书是苏黎世联邦理工学院的教材。它讲述了多种分布式系统中会用到的算法。虽然分布式系统的不一样场景会用到不一样算法,但并不表示这些算法都会被用到。不过,对于学生来讲,掌握了算法设计的精髓也就能触类旁通地设计出解决其余问题的算法,从而获得分布式系统架构设计中所需的算法。
本书覆盖的算法有:
这些算法对你迈向更高级更广阔的技术领域真的至关有帮助的。
这本书的书名直译过来是在有软件错误的状况下,构建可靠的分布式系统,Erlang之父乔·阿姆斯特朗(Joe Armstrong)的力做。书中撰写的内容是从1981年开始的一个研究项目的成果,这个项目是寻找更好的电信应用编程方式。
当时的电信应用都是大型程序,虽然通过了仔细的测试,但投入使用时程序中仍会存在大量的错误。做者及其同事假设这些程序中确实有错误,而后想法设法在这些错误存在的状况下构建可靠的系统。他们测试了全部的编程语言,没有一门语言拥有电信行业所须要的全部特性,因此促使一门全新的编程语言Erlang的开发,以及随之出现的构建健壮系统(OTP)的设计方法论和库集。
书中抽象了电信应用的全部需求,定义了问题域,讲述了系统构建思路——模拟现实,简单通用,并给出了指导规范。阿姆斯特朗认为,在存在软件错误的状况下,构建可靠系统的核心问题能够经过编程语言或者编程语言的标准库来解决。因此本书有很大的篇幅来介绍Erlang,以及如何运用其构建具备容错能力的电信应用。
虽然书中的内容是以构建20世纪80年代的电信系统为背景,可是这种大规模分布式的系统开发思路,以及对系统容错能力的核心需求,与互联网时代的分布式系统架构思路出奇一致。书中对问题的抽象、总结,以及解决问题的思路和方案,有深入的洞察和清晰的阐释,因此此书对如今的项目开发和架构有极强的指导和借鉴意义。
这是一本很是好的书。咱们知道,在分布式的世界里,数据结点的扩展是一件很是麻烦的事。而这本书则深刻浅出地用不少工程案例讲解了如何让数据结点作扩展。
做者马丁·科勒普曼(Martin Kleppmann)在分布式数据系统领域有着很深的功底,并在这本书中完整地梳理各种纷繁复杂设计背后的技术逻辑,不一样架构之间的妥协与超越,很值得开发人员与架构设计者阅读。
这本书深刻到B-Tree、SSTables、LSM这类数据存储结构中,而且从外部的视角来审视这些数据结构对NoSQL和关系型数据库所产生的影响。它可让你很清楚地了解到真正世界的大数据架构中的数据分区、数据复制的一些坑,并提供了很好的解决方案。
最赞的是,做者将各类各样的技术的本质很是好地关联在一块儿,帮你举一反三。并且抽丝剥茧,循循善诱,从“提出问题”,到“解决问题”,到“解决方案”,再到“优化方案”和“对比不一样的方案”,一点一点地把很是晦涩的技术和知识展开。
本书的引用至关多,每章后面都有几百个Reference。经过这些Reference,你能够看到更为广阔更为精彩的世界。
这本书是2017年3月份出版的,目前尚未中译版,不过英文也不难读。很是推荐。这里有这本书的PPT,你可从这个PPT中管中窥豹一下。
在今天的文章中,我给出了一些分布式系统的基础理论知识和几本很不错的图书和资料,须要慢慢消化吸取。也许你看到这么庞大的书单和资料列表有点望而却步,可是我真的但愿你可以花点时间来看看这些资料。相信你看完这些资料后,必定能上一个新的台阶。再加上一些在工程项目中的实践,我保证你,必定能达到大多数人难以企及的技术境界。