到目前来看整个架构的演变过程是从单体到水平垂直扩展,再到 SOA 设计,领域驱动设计,事件驱动设计,后到云原生的架构和优势,如果在技术网站甚至微信公众号就可以搜到一堆关于这些的架构设计说明,现在我想换一种方式,从某个点出发,某个概念出发,某个模式出发给大家讲解,然后大家就可以通过这些点的认识扩展到面上甚至,对分布式也有了比较立体的架构思维。
今天主要介绍了不要只顾单体架构的罪孽而忽略了可扩展性这个根本问题,当然分布式架构也有很多问题,也要讲解一下分布式架构面临的主要挑战,也会介绍服务无状态设计给弹性伸缩带来多少好处,一会介绍一下异步通讯的隔离设计,也会有弹性设计的熔断器,这节课没有深刻的技术知识,也没有很多生涩的名词,就是让大家学习如何理解和设计一个分布式架构。我也尽量通俗化的语言进行说明。
单体架构之
现在说一说单体架构,一说到单体架构好像就立马在人堆里找到了犯罪凶手,立马在地上划一道线和单体架构划清界限,单体架构有什么罪呢?我简单给大家罗列了一下,这个所谓的单体架构到底给大家带来多大的麻烦。
,功能难以拆分,在说明难以拆分之前我先说一下为什么要实现功能的拆分,我们知道客户提出业务需求,技术按照需求来开发设计功能这个没问题,但是一个系统中分为很多功能的,比如微信的注册登录功能,刷新朋友圈状态功能,提交发布朋友圈功能,给文章点赞功能,甚至发红包抢红包功能,有些功能访问量就很高,比如用户一直在线功能,刷新朋友圈功能,需要高吞吐缓存客户登陆状态,高性能高吞吐量实时获取朋友圈状态,这就造成了功能需要拆分的需求,对吞吐要求高的进行拆分,只有拆出来才可以正确对待,才可以对症下药,才可以正确扩展针对的优化,如何设计一个容易扩展的架构那就是好设计成无状态的应用,后面我会介绍服务的无状态设计。
第二,技术难以演进,且说一下技术为啥要演进,比如说你们公司开发了一个客户管理系统,但是以前是使用一个源代码包,后来客户越来越多,技术发展也越来越快,以前简单的 MVC 架构现在需要依赖反转设计,也有了开发更加容易的 Spring 架构,甚至有数据库的缓存技术,异步的消息中间件技术,这些技术红利单体架构很难享用的到的,如果非用不可,技术演进难度会指数级增加,就代码版本维护这一项就很管理,开发人员痛苦的事是什么?就是看别人的代码,而且越到后面理解这一堆代码的难度就指数级增长,这里面重点是业务和技术都是随时变化的,我们要从变化从架构从人的角度上考虑技术的选型以及架构设计,如何面向人的架构设计是一个更高的命题。
第三,系统难以维护,单体节点上的软件通常不应该出现模棱两可的现象:比如说,当硬件正常工作时,相同的操作通常会产生相同的结果(即确定性);而如果硬件存在问题(例如,内存损坏或糖口松动),结果往往是系统性故障,如内核崩溃,蓝屏死机,启动失败等。因此在单节点上一个质量合格的软件状态要么是功能正常,要么是完全失效,而不会介于两者之间,但是有这么一个事,我们作为运维维护系统,大部分都是为了面对快要不可用之前预料故障解决故障,在生死边缘让系统正常运作不会影响到前端客户的体验,运维就是要保证即使出错的情况下我们也可以让系统大程度的可用,如果单体节点,那这种运维方式就玩不了,运维的空间就不多,所能做的事情也有限,没有发挥空间的余地,很难维护。
这里关于维护的事情多说一句,其实在这个逻辑的背后涉及计算机设计一个非常审慎的选择:如果发生了某种内部错误,我们宁愿让计算机全部崩溃,而不是返回一个错误的结果,错误的结果往往更难处理。因此,计算机隐藏了一切模糊的物理世界,呈现以一个理想化的系统模型,像以数学完美的方式运行。CPU指令通常以确定性方式操作,如果写入一些数据到内存或磁盘,那么这些数据将保持不变且不会被随机破坏。这种确定-正确性的设计目标可以一直追溯到台数字计算机。
然而涉及到多台节点时,情况发生了根本性的改变。尤其对于这种分布式系统,理想化的标准正确模型不再适用,我们必须面对一个现实,系统越来越混乱,各种各样的事情都可能出错,后面我会讲解把避免故障隔离故障当做一个功能设计。
第四,性能难以扩展,我们接业务需求的时候,我们开发了业务需求的功能,但是非业务需求的功能呢?什么是非业务需求?必须要非业务需求的功能吗?还是那句话业务越来越多,需求越来越细,我们要拆分功能是必要的,比如说注册和登陆功能,首先这两个功能要保证顺序一致性的,而且登陆需要保存客户登陆状态的也就是数据的状态,如果我们扩展用户登录的服务,就需要保证这个服务具有扩展性,但是用户登录的状态如何保存呢,如果实现自动扩展有数据状态的服务,其实无状态的服务容易弹性伸缩,我们要如何保障无状态呢?这些就是非功能需求,这里多说几句,大家知道PaaS吧,PaaS其实很难设计,为什么呢?就是因为我们要再设计系统之初就要保证这些非功能需求的实现,达到这种情况,达到客户可以不用考虑或者感知不到这些非功能设计而让客户达到满意的效果,后面我使用AKF立方体的方法给大家介绍如何达到可扩展性。
第五,艰难自我救赎,单体节点也不是完全达不到高可用,那就是模块化的水平扩展,就是部署多个单体模块前面加一个负载的应用对很高的流量进行均衡,但是这种方法有很大的问题首先这个流量高是高在哪里,如果只是恰巧客户在做抢红包活动,只是一个抢红包的功能我们就要水平扩充几十个应用包吗?况且应用包扩容了我们要实现数据库的读写的,数据库都是有状态的,我们如何大限度无感知的扩容,但是这里边我就要说一下 AKF 立方体了,AKF 的这个工具可以帮助大家正确理解这个问题并且找到大家都认同的方法。
后,说了这么多的单体架构的罪,单体架构真的无用武之地,不是的,上面的我罗列的几个事项,是因为业务变化,技术变化,市场的变化导致要求越来越高了,如果需求简单,我觉得单体架构的模式还是可以使用的。
如何理解扩展性之AKF立方体
说了这么多,你们是不是要问了,我们今天的课程也是和网上的文章一样先贬低单体架构在突出分布式架构吗?其实不是的,我上面给大家罗列了所谓单体架构的罪孽,但是结果是我们依然不知道如何 界定什么是单体架构,但是我们依然想和他永远的划清界限,其实问题的产生都是因为我们在设计架构的时候,没有从扩展性这个根本性的问题考虑,我们说好多单体架构的坏话,但是单体本身没错,错的是我们对架构的理解方式,如何建立一个适应业务变化,技术变化,市场变化的架构呢,如何理解这种具有扩展性的架构呢?,如何设计一个可以扩展的架构?如何让所有人都认同这种扩展模式呢?我这里使用了 AKF 立方体的设计模式,AKF 是什么呢?
AKF 扩展立方体(AKF Scale Cube)是一个描述从单体应用到一个分布式可扩展架构的模型概念,他可以完美地从概念上和理解上从单体到分布式架构完美过度,还很容易形成一门普世的架构语言模型,比较容易让所有人理解并且认同。简单来说,立方体,就是一个三维的空间,我们终要表达的是 “可扩展”性,那么没有比三维的空间更能表达扩展性的了。
AKF立方体是一种理论指导,它可以帮助我们在各个方向上做扩展。X、Y、Z三个方向都给我们指明了道路,让我们在执行的时候可以在不同的方向上做一个参考,参照这一理论模型来确认我们所做的扩展动作是否符合这一理论,就是提出一个方案,然后公司很多不同职位的人通过 AKF 立方体进行评估。
首先我通过举例子的方式给大家说明一下 AKF 立方体的 X 轴,Y轴,Z 轴的设计,AKF 扩展立方体的X轴代表无差别地克隆服务和数据。我们用人和组织是来说明这种分割的方式。让我们先回想一下在过去靠打字小组(typingpool)来处理会议纪要、信件、内部备忘录等的情形。这种打字的工作被送到打字小组,然后几乎无差别地分配给每个打字员。这种工作分配就是一个完美的 X 轴扩展实例。简单来说当我们需要执行更多任务时,只需添加更多的克隆实体就可以了,X 轴看起来似乎很伟大!如果备忘录的数量超过了当前的打字能力,只需添加更多的打字员就 OK 了。但是问题来了,有一个 X 轴就可以了吗?为什么扩展立方体还需要其他两个轴呢?事实情况是,让我们先回到打字小组这个例子来回答这个问题。为了要完成备忘录、对外函件和笔记的打字,不同类型的文件打字员需要某些知识。随着公司的发展,假设打字小组提供的服务量要求增加了,就是打字效率和打字成品完美度提高了。而现在有 100 种不同类型和格式的打字服务,而这些服务的分布并不是均匀的,90% 的工作是需要普通知识的类型,10% 的工作需要别的专业知识的类型。打字员可能会很快完成那些常见的任务,如果需要花些时间查找那些不常见的格式,就会减缓整个任务流水线的速度。
这个时候就需要 Y 轴分割了,AKF 扩展立方体的Y轴代表的是按照交易处理的数据类型,交易任务类型或两者的组合进行分割,也就是按照工作职责和服务分割。理解这种分割的一种方法是对行动的责任进行分割。我们从汽车“加工车间”发展到更加专业化的系统来看,正如亨利·福特在汽车制造装配线上所做的那样。与其安排 100 个人制造 100 辆独特的汽车,每人完成 的造车任务,不如让 100 个工人执行子任务,如发动机安装、喷漆、安装挡风玻璃等。按照职位职责分割就是 Y 轴的逻辑,Y 轴分割的好处很多。不但有助于管理,也有助于扩展那些需要掌握某些信息才能处理交易的工作,意味着人员和系统可以更加专业化,从而提高每个人或每个系统的吞吐量。其实这就是一个简单的Y轴分割,也会带给我们简单的故障隔离。这种隔离类似于服务之间的隔离,有些类似 SOA 设计,面向服务设计。
扩展立方体的 Z 轴通常基于请求者或者客户的信息进行分割。对打字服务组进行 Z 轴分割时,我们既要了解请求打字的人,也要了解分配到任务的打字员。在分析打字工作请求时,我们可以看看提出独特工作或代表特殊工作量的人群。有些类似 VIP 客户的需求和专家组的工作分割。比较起来其实z轴也具有一定的用户隔离性,就是按照用户使用情况的隔离,这种隔离和架构中的多租户模式非常相近,虽然多租户模式有多种实现方式,对服务和数据不同程度的共享,可能租户之间服务不共享,但是数据共享,也可能相反。但是总的来说相对客户来说是隔离的。
总的来说,X 轴代表对服务和数据的复制,Y 轴代表按照职责和服务进行分割,Z 轴代表基于客户或者请求者分割。
但是我想说什么呢?这种 AKF 立方体有什么用呢?每个轴都有扩展性的属性,而且每个轴都有益于形成简单的概念化,这些角度的变化更加有助于我们从单体扩展到分布式的理解。这种扩展性是随着我们对技术的理解,对业务的理解,对产品的理解,对流程的理解,对服务的理解是逐步同步加深迭代的,理论上x轴这种工作小组式的扩展在技术上可以无限水平扩展的。
但是从业务角度来看,业务更关心应用中的服务是否正确正常高效高质量的运行,单纯 X 轴扩展做不到,这就需要从 Y 轴对服务功能的拆分,对高效率高质量的扩展,而且从 Z轴可以按照现实情况,按照地域要求,按照客户的特殊要求进行扩展,类似的如数据持久化到硬件的扩展,其实大家可以想一下,有 X 轴无差别的扩展,势必影响到 Z 轴要求的特殊情况的扩展,否则 Z 轴就会成为瓶颈,Z 轴扩展的成本可能高一些,但是回报也是很高的。
所以技术开发的服务要尽量契合产品业务需求的,迎合使用者的,提高效率的,提高质量的,而且还要处理特殊请求的比如数据存储的状态,从过程上来说无论是大部分可以标准化的或者特殊的都可以尽量满足流程的扩展性的。
就是说这里的扩展不只是技术架构的扩展,也是针对业务需求的扩展,也是产品的完善;流程的标准化自动化;平台的高可用性,稳定性以及资源复用和成本管控。
简单来说,技术可以使用 AKF 它了解架构的可用性和可靠性;业务使用它可以连接业务的流程和交易价值,产品使用它可以充分了解他的优势和适应性。
按照上面这张图片所示,如何对微服务架构中的拆分 立方体的 X 轴扩展就代表水平复制,通过复制节点,实现多个节点同时提供服务,从而大大提高系统的总体容量、解决单点问题等。比如 nginx 反向代理多个服务节点,实现多个应用的负载和流量均衡,进而扩大吞吐量,但是这种服务的可伸缩性,好把服务设计成无状态的,后面我会讲解无状态,Y 轴扩展从业务层面来考虑扩展方式,因此又称为业务扩展或资源扩展,这点基本上就等同于微服务化。系统从业务层面拆分为多个子系统,各子系统由单独的团队负责整个生命周期的维护,单独部署运行,而且子系统间具备故障隔离的能力,再说一下 Z 轴扩展,Z轴扩展一般用来扩展数据库,把那些优先处理的数据和服务按照多租户的模式进行资源一定程度的共享,更多处理的是数据的状态。(数据库分片)在做某些查询的时候,查询指令被送给每一个分片,然后分别查询,终获得的结果聚合之后返回。大概是这样。
从实际情况触发,从 Y 轴扩展按照服务分割,比如登陆是一个服务,注册又是一个服务,但是每个服务中都有些不同程度的重复使用,如果登陆的人数增长了许多,所以 Y 轴扩展中有x轴扩展,就是把登陆服务进行水平扩展,以应对那些增加的用户登录,但是登陆服务要保存服务的状态和数据的状态,如果数据状态缓存在数据库中,这就要需要对数据库读写的扩展,数据库一般是读多写少,为了读取方便可以设置多个分片,有的分片副本作为写,有的作为读,这样的扩展性从技术上来看就比较清晰了,X 轴,Y 轴,Z 轴都有了关联性的联动的扩展。
X 轴偏技术考虑,Y 轴偏业务考虑,Z 轴偏现实特殊情况考虑,无论从业务还是技术角度去看,都可以理解 AKF 的所要表达的每个轴的独立扩展性和多个轴的共同扩展的相关联性,技术和业务可以独自沿着自己的道路迭代扩展,也可以在考虑现实的情况下,业务和技术2条腿的方式交替前行,交替扩展。
但是 AKF 这个设计除了解决扩展性问题还有一个用处,同时也减少了变更的风险。
如何说清楚这个变更的风险呢?大家都知道过程吧,美国人很喜欢自动化的,比如说亚马逊 AWS 云计算有几千万的服务器但是整个云计算才有2万人不到,这个意味着什么呢?意味着一人平均管理几千服务器,但是他们很喜欢搞自动化,全部尽量自动化来管理这些机器,如何自动化呢,他们认为能自动化的就可以结构化,可以结构化的就必须说的清楚整个过程,并且形成标准化,有了清楚的过程和标准的过程意味着什么,意味着你遇到问题可以不用请示别人甚至领导,可以不用自己主观判断直接按照标准执行就可以了,所以过程一定要清晰这是标准化结构化自动化的要求。
如果有一个新的过程,可以把它交给多个开发团队中的某一个开发团队,然后分析这个过程,确定由这个团队中的哪个功能职责部门负责,或者某个专门的部门负责。你就可以看到这个新的过程是如何与有限的一群人一起工作的,然后进行度量,以决定是否把它推广到其他的团队,当然在推广之前要不断的修改和优化。我们就使用 AKF 立方体模式去规避过程的风险,它可以作为任何围绕可扩展性话题进行讨论,AKF 作为这个讨论的基础,因为它有助于在一个组织中创造一种工程师之间的共同语言,无论是技术还是业务。但是不是谈论特定的方法,团队可以专注于概念,而且X 轴,Y 轴,Z 轴无论我们如何选择都是很容易概念化的,这样就很容易在很多人中形成一个共识的理论基础,并可能由此演变成任意数量的方法。进而尽量去定义这个过程,并且可以形成标准推广出去,这就是 AKF 的重要的作用。
说了这么多,前面我们讲解了单体架构,后面那就是分布式架构了,我想说的对于扩展性的理解而言使用 AKF 的方式更加具有说服性,对扩展性的表达和描述更有力度。
但是 AKF 设计只是一个可扩展模型的概念,有什么问题呢?容易造成为了扩展而扩展,而忽略了分布式架构的问题和挑战,因为我们在真正使用的过程中我们会遇到很多故障,有些故障直接导致服务的全面失效导致终的不可用,这就很严重了,所以我们要尽可能把所有故障都要预料到并且有效的避免或者减轻故障,后可以尽快的恢复服务正常运行,无论是什么故障我们的目标还是一致的,那就是尽快恢复系统。
再说如何面对故障的设计,如何面对恢复的设计之前先说一下无状态的一些知识,前面说了按照 X 轴扩展好是设计成无状态的服务。
什么是无状态服务?这里我详细说一下,一个应用或者协议都不使用状态称为无状态应用(stateless),http 就是一个无状态协议,因为他不需要请求之前的任何信息他就知道如何满足下一个请求的一切信息,用户登录的会话机制如果保存在后台中,就是一个有状态的应用,这种有状态应用可以使用户和服务器之间保持一种非常紧密的关系,因为只有这台服务器保存了用户会话信息,很难通过水平扩展服务器来做高可用,所以无状态本质上就是相同的用户行为在后端关闭原来的服务进程,而新启动的服务进程也可以达到相同的效果而前端无感知,作为一个具有扩展性的架构,无状态很容易达到弹性扩缩容的。
无状态的服务其实和这个“函数式编程”的思维方式如出一辙。在函数式编程中,一个铁律是,函数是无状态的。换句话说,函数是 immutable 不变的,所有的函数只描述其逻辑和算法,根本不保存数据,也不会修改输入的数据,而是把计算好的结果返回出去,但是,现实世界是一定会有状态的。那如何设计在这些有状态的架构呢?为了做出无状态的服务,我们通常需要把状态保存到其他的地方。比如,不太重要的数据可以放到 Redis 中,重要的数据可以放到 MySQL 中,或是像 ZooKeeper/Etcd 这样的高可用的强一致性的存储中,或是分布式文件系统中。
于是,我们为了做成无状态的服务,会导致这些服务需要耦合第三方有状态的存储服务。这就造成了一方面是有依赖,另一方面也增加了网络开销,导致服务的响应时间也会变慢。所以,第三方的这些存储服务也必须要做成高可用高扩展的方式。而且,为了减少网络开销,还需要在无状态的服务中增加缓存机制。然而,下次这个用户的请求并不一定会在同一台机器,所以,这个缓存会在所有的机器上都创建,你看有状态的应用处理起来很麻烦的。
但是终这些方案大部分将服务和存储分离,也是为了让自己的系统更有弹力。服务做到无状态高扩展性了,那么数据的存储节点呢?只要有数据持久化就会有状态,如何把数据存储做到具有很强的扩展性,要解决数据结点的 Scale 问题,也就是让数据服务可以像无状态的服务一样在不同的机器上进行调度,这就会涉及数据的 replication 问题就是数据的副本分片。而数据 replication 则会带来数据一致性的问题,因为副本意味着用户保存一份数据数据库就要保存多份的副本,这就需要保证多个副本时候可能要保障数据一致性和时间一致性,做不到就可能数据丢失,进而对性能带来严重的影响。说实话,要解决数据不丢失的问题,只能通过数据冗余的方法,就算是数据分区,一份数据分成多份,每个区也需要进行数据冗余多个副本处理,这就是数据副本。
当出现某个节点的数据丢失时,可以从副本读到。数据副本是分布式系统解决数据丢失异常的手段。
总的来说:要想让数据有高可用性,就得写多份数据。写多份会引起数据一致性的问题。数据一致性的问题又会引发性能问题。在解决数据副本间的一致性问题时,就有分布式事务各种解决方案,太乱了一切都变成了架构师的trade-off。
这里多说几句,也有人说真正完整解决数据扩展性问题的应该还是数据结点自身。只有数据结点自身解决了这个问题,才能做到对上层业务层的透明,就是说业务层可以像操作单机数据库一样来操作分布式数据库,这样才能做到整个分布式服务架构的调度。也就是说,这个问题应该解决在数据存储方,业务层不管这个事该保存的保存,该查询的查询就像操作单机数据库一样。但是因为数据存储结果有太多不同的 Schame(数据库对象),所以现在的数据存储对象也是多种多样的,有文件系统,有对象型的,有 Key-Value 式,有时序的,有搜索型的,有关系型的…文章来源:https://www.toymoban.com/news/detail-852795.html
这就是为什么分布式数据存储系统比较难做,因为很难做出来一个放之四海皆准的方案。所以,真正解决数据结点高可用高性能稳定性的方案应该是底层的数据结点。在它们底层上面做这个事才是真正有效和优雅的。我相信状态数据调度应该是在 IaaS 层的数据存储解决的问题,而不是在 PaaS 层或者 SaaS 层来解决的,就是说,我们只需要一个底层是分布式的文件系统,增加新的结点只需要做一个简单的远程文件系统的 mount 就可以把数据调度到另外一台机器上了,这样就做到了上面说的服务和存储的分离。文章来源地址https://www.toymoban.com/news/detail-852795.html
到了这里,关于云原生系列之分布式架构设计要点的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!