AlexiaChen / AlexiaChen.github.io

My Blog https://github.com/AlexiaChen/AlexiaChen.github.io/issues
87 stars 11 forks source link

创世记-----什么是架构以及架构基本法 #77

Open AlexiaChen opened 4 years ago

AlexiaChen commented 4 years ago

什么是架构?

Linux内核有架构,MySQL有架构,JVM有架构,微信有架构,支付宝有架构,我们平时所说的架构到底是谈什么类型的架构?

当然,从上面所说的提问,我们知道了,架构一般泛指软件系统更加高层的设计或者组织方式,是更加宏观的层面,而不是代码细节层面,比如类的关系组织。

架构也有很多层次:

系统与子系统

系统(软件系统)一般是一堆有关联的实体组成,通过某种规则运行,完成单个实体不能单独完成工作的群体。当然,子系统也是一样的,也是一群有关联的实体组成的系统,一般是大系统的一部分,看角度了。大致是一个树形结构。

举个例子:

子系统追溯到叶子节点的时候,一般就是模块与组件了,因为工程量,规模或许是其他维度的原因,只能是组件,模块规模了。

模块与组件

框架与架构

框架与架构是非常相似的概念,是强关联关系。

当然,很多网络上,框架和架构有些是混用的,这个视情况而定,它们不一定是错的,在特定的上下文里理解了就行。

架构目的

架构设计误区

Q: 不做架构就运行不起来吗?

A: 显然不是,创业公司初始产品一般是不太做架构设计的,基本是单体应用开始撸代码了,不一样跑的好好的?

Q: 做了架构设计就能提升开发效率?

A: 不是,架构设计是需要耗费时间和人力,这部分投入如果用来尽早编码,可能会更快。微服务架构适合创业公司吗? 显然不适合。

Q: 设计良好的架构能促进业务发展?

A: 貌似有一定道理,微信的架构好吗?照超过来能成功吗?显然不是。架构是从实际出发,分析问题本身而得到的结果。不然就是PPT架构师。

Q: 为高性能,高可用,高扩展,所以要做架构设计,是不是?

A: 恩,有一定工作经历了,确实互联网很多架构是奔着这几个目标设计的。但记住,这样的系统是慢慢根据实际情况迭代出来的,不是一开始架构设计出来的,一开始就奔着这个目标,是会给团队带来灾难的。

历史的发展

最终,软件架构。 也就是随着软件规模增加,计算相关的算法和数据结构不再构成主要的设计问题(数据结构和算法大部分已经固化到基础设施上,比如MySQL,Redis,基础库和框架等),当系统是由许多部分组成,整个系统的组织或架构,导致了一系列的新设计问题。比如,内部耦合严重,不好维护等。

所以,架构设计的主要目的是为了解决复杂度带来的问题。你也看出来了,后面文章所说的架构一般是业务系统的架构,不是基础设施实现的架构。虽然一些架构思想是一样的。当然,业务架构和可以简单理解为,把开源组件,通过物理+逻辑的组织方式调整布局,或进行二次开发,来达到你想要的目的。这么理解可能比较low,不过这就是大多数互联网的套路,比如大数据系统什么的。

复杂度来源

高性能

打算从两方面来说,一个单机内部为了高性能带来的复杂度,另一个是集群为了高性能带来的。

单机复杂度

单机的一些高性能,一个是操作系统相关的高性能API,另一个就是对多线程,多进程的模型做合理设计和取舍来适应多核CPU设计高性能,很繁琐。目前多核方案是SMP方案。Nginx是多进程,Redis单进程,memcached是多线程,这几个都单机都实现了高性能,但是内部实现差异巨大。所以复杂。吃力不讨好,一般做业务架构,不往这方面考虑。

集群的复杂度

通过大量机器提高性能,并不是增加机器这么简单,比较复杂,所以业务架构往往是优化集群复杂度,不知道大家能不能理解。支付宝等支持高并发的系统,后面可能是几万台机器。这么多的机器看起来貌似是堆机器堆上去的性能,但是如果是普通的人来做,可能十几万,几十万都达不到它们的性能。

所以,这节会着重围绕集群复杂度来讲。

从单个服务器,增加到多个时,就需要有个全局的分配器出现,来处理任务的分发,分配器可能是硬件设备(F5,交换机等),也可能是软件网络内核(LVS,就是章文崇博士写的这个,阿里P11,滴滴技术副总),也可能是负载均衡软件(Nginx,HAProxy)。

分配器的算法选择,轮询,权重分配还是服务器负载分配?如果是负载分配,业务服务器还要上报自己的负载状态给分配器。随着任务量的要求,业务服务器不断增加,但是始终会有瓶颈的,瓶颈就在分配器是单台,最后分配器也要弄一个集群。麻烦大了。架构突然复杂。

分配器变成集群以后,带来的问题就是要将不同的用户分配到不同的分配器上,常见的方法是DNS轮询,智能DNS,CDN,GSLB全局负载设备等。关系一旦变成多对多,那么状态管理,故障处理复杂度也会陡增。

之前的任务分发方式,能突破单台机器的极限,可以满足一定量的业务需求,但是,单纯通过这种扩展,收益会越来越低。主要原因是,你既然想通过加机器,那么就说明你的用户量多了,业务和需求越来越复杂,单体应用代码也越来越复杂,单台机器的性能会越来越低。

这时候需要用业务拆分的手段进行分解,就是以业务来拆分,从逻辑上把大业务拆分成子业务,比如微信中的子业务,朋友圈,漂流瓶,评论系统啥的。把业务拆分成小的来配合。走SOA的路线,子业务之间通过RPC来通信。

从业务角度看,业务拆分不会减少功能,也不会减少代码量(代码量可能还会增加,比如增加RPC模块啥的),但是为什么这样做就可以提高性能了呢?

是不是拆分粒度越小越好?也不是。拆分的太小,表明子业务增加,子业务之间的通信RPC是走网络的,子业务太多的话,时间成本会加大。要适可而止。从实际业务考虑来拆分,把握一个粒度。

高可用

就是字面意思,很高的可用性,就是系统7×24小时不间断的运行,关键是“不间断”,怎么做呢?软件,硬件都会有BUG,故障。Google每天有会硬件故障,在那么大的集群下。

所以,高可用方案是五花八门,但是万变不离其宗的是,都是通过“冗余”来实现的,通俗点说就是加机器,把数据A,分别冗余备份在多台机器上。跟高性能的方案确实有点像,区别在于,高性能增加机器是增加处理器性能,高可用是增加机器为了“备份”冗余的数据或者计算资源。

这里主要是业务逻辑的处理,是CPU密集的。前面提到了,同样是增加机器,那么还会有一个机器作为一个任务分配的角色,这也是复杂度的体现,任务分配器需要增加分配算法,常见的双机算法有,主备,主主。主备又分为冷备,温备,热备。

双机是比较简单的,更复杂的还有高可用集群,分配算法更加复杂,有1主3备,2主2备,3主1备,4主0备,这几种方案没有最优,只有合适,需要结合业务。Zookeeper采用的就是1主多备,memcached就是全主0备。

存储与计算的高可用区别在于:它需要将数据从一台机器移动到另一台机器,需要经过线路传输。线路传输大概在毫秒级:同一个机房,在一台交换机上内部能够做到几毫秒。分布在不同地方的机房耗时大概是几十到上百毫秒。

看起来以上的数字还挺快的,其实对于高可用系统来说,意味着,在某一小段时间内,数据肯定是不一致的,数据不一致,就意味着业务表现不一样。而且线路也会出现故障。2015年支付宝因为光缆被挖断,业务就受到了巨大影响。所以无论如何,请把传输这个先当作是不太可靠的。

不做“冗余“,达不到高可用,数据做“冗余”,那么就肯定有传输的不可靠问题,传输不可靠带来的就是,某段时间内的数据不一致。所以存储高可用的难点就是,假设一定会存在数据不一致的前提下,怎样减少或者规避数据不一致对业务造成的影响。而不是数据怎样备份。

另外,存储高可用需要考虑CAP原理,强一致性,高可用性,强分区容错性,大概只能同时满足2个100%,最后一个可以尽量提高。但是达不到100%。

状态决策是啥?就是你的系统要能判断当前系统(一般是集群)的整个状态是异常还是正常,如果出现异常,就需要采取行动来保证高可用。看样子需要有一个元数据的管理决策者,类似于leader,聪明,这里确实开始涉及到了分布式系统的共识问题(非拜占庭网络)。下面列举几个方式:

  1. 独裁式

只有一个决策者,结果是不会出现决策混乱,但是前面说了,网络,机器长期下来都有故障,这个方案单点故障问题出现就废了。

  1. 协商式

最常见的就是主备切换,这个比独裁好些,但是一旦网络出现问题,状态决策怎样做?哪个为主哪个为备?总会出现问题的。

  1. 民主选举式

当今你接触到的几个常见的共识算法都是民主的,比如Paxos,Raft,ZAB。就是通过多个独立的个体投票来选举leader进行状态决策。主要是多个独立个体,即使出现网络问题,一般也会出现大多数取胜,所以容错高了不少。民主的本质就是少数服从多数。

当然,民主决策有个问题就是脑裂。就是一个集群因为连接中断等原因,造成了两个独立分隔的子集群,每个子集群单独进行选举,于是选出了两个leader,相当于有了两个大脑,所以为了解决这个问题,民主决策的系统一般都采用”投票节点数必须超过总节点数一半“的规则来处理。

以上的方式虽然解决了脑裂问题,但是同时降低了系统整体的可用性,如果系统不是因为脑裂问题导致投票节点数过少,而真是因为节点故障,此时系统是不会进行选举的,整个系统相当于宕机了。

所以,无论采取什么方案,状态决策都不可能做到任何场景下都没有问题,但完全不做高可用方案又会有更大问题,所以要有trade-off。

可扩展性

这个主要是因为业务的变化了。大部分是靠自己的经验和直觉。

应对变化的方案就是拆分,分离出变化层和稳定层。然后两层之间需要设计一组接口,一般的实践就是《设计模式》将的那套东西了。这方面多套用,多思考别人的模式就可以了。

低成本

当我们设计”高性能“,”高可用“往往是通过加机器来实现的,但是低成本往往又是减少机器,它们本质上是冲突的。这个也需要权衡了。一般是设定一个成本目标,比如不能超过XX上限,花XX钱的前提下,设计”高性能“,”高可用“的方案,评估方案是否符合成本,不行就重新设计架构,如果实在不行,那就只能找老板沟通了。

低成本给架构设计带来的挑战主要是,往往只有”创新“才能达到低成本目标。这”创新“既包括了开创一个全新的技术领域(对于绝大部分公司要求过高),也包括引入新技术,找不到新技术,只有自己开创了。比如阿里巴巴去IOE,自研OceanBase,魔改InnoDB等等。

新技术的例子很多,大部分公司基本是用这样现成的,比如:

自研创新的例子:

引入新技术的复杂度主要是需要团队去熟悉新的技术栈,并跟已有的系统集成。创造新技术难度在于创造全新的理念和技术,与旧的相比需要有质的飞跃,比如Flink 超越了 Spark。

所以显然后者难,一般的小公司靠引入新技术来达到低成本,高开发效率的目标。不应该自己造轮子。

架构设计原则

一旦说起架构,本质上就是不确定性的,这个人认为可以这样,另一个认为这样不行。一旦涉及”选择“,就可能会让人陷入两难,比如,后端数据库用MongoDB还是MySQL,前端用Vue还是React?

其实不用纠结,架构设计可以遵循以下原则。

合适原则

合适优于业界领先。

如果不遵循这个原则,一般以失败告终,不外乎以下几个原因:

简单原则

简单优于复杂。

程序员毕竟是有些技术情怀的,追求业界先进,追求炫酷的新技术,所以如果你作为一个架构师,不要被你的队员带歪,不要被有意无意地带领团队走向复杂,你心中要有个指标,就是复杂性。复杂性尽量选择简单的,至少是你当前认为更简单的。设计一个主备方案,如果用心跳来实现互相报告状态,可能大家认为太简单了,引入ZooKeeper来做主备决策,大家可能认为才高大上些,你看ZAB共识协议,多么高大上,但是真正理解ZAB协议的普通程序员很少。

组成系统的组件更多意味着更复杂,同时它们的互交关系也更复杂。你可以想像一个图结构,一个节点的出度入度很多,脑袋开始理不清楚了。出问题太难排查了。

意思就是单个组件承担了太多的功能。单体应用。往往随着需求增加都会变成这样。

之前说的Zookeeper就挺复杂的,它的功能本身是分布式选举,采用ZAB协议,功能简单,但实现复杂,相比之下,etcd就更简单些,因为etcd采用的是Raft协议,Raft协议更加容易理解和实现。你看,PingCAP这家数据库公司选择的就是etcd翻译过来的Rust代码实现的Raft协议。如果好奇他们为什么这样,可以去他们网站上看他们写的博客。

KISS原则总是在哪里都有用的。

演进原则

演进优于一步到位。

阿里巴巴有篇文章写的这样一句话:”好的系统架构是迭代出来的,不是设计出来的“。 请体会下,说的就是这个意思。主要因为,软件系统唯一的不变就是变化,拥抱变化,才能主宰未来。所以是好的软件是演进出来的。没见过世界上任何一个知名开源软件的仓库的第一个commit上去的代码,架构或技术上就很牛逼,有些质量可能真不敢恭维。

只要记住这几个原则就好,不要被炫酷的东西暂时迷惑住了双眼就好。

架构设计流程

首先,从这里开始,普通程序员要明白,架构设计的思想无处不在,不要总觉得架构师很牛逼,可望不可及。我觉得架构师这个title,可能是国内互联网的流行(因为真的太火了),给各位造成了些误解。因为国内的架构师,一般是要应对所谓的”三高“ (高可用,高并发,高性能)。比如,XX架构师好牛逼,带团队实现了XX系统,双十一抗住了XX量级。这可能是阿里带出来的节奏。你看国外的Google,Microsoft等顶级公司,招聘的JD上就很少写架构师(Architector)这种岗位,David Culter老爷子是架构师吗? 他可不是。至少title不是。但是他不比国内互联网大厂的P8,P9什么的架构师牛逼多了?微软最高技术级别。阿里的多隆目前暂时比不过。有些人把多隆比作中国的Jeff Dean,我觉得就目前的展现出来的成果,多隆比Jeff Dean差得还是天远。Jeff Dean的技术栈,可能全世界也没几个达到,不是单纯个人技术比较。

所以说架构师是个很虚的概念,但是架构设计是个很真实的概念,软件系统哪里都需要架构,只是国外的一些大牛人家没天天说自己是架构师而已。

说了那么多,回归下正题。架构设计是有套路的(特别是互联网后端的业务架构,大数据架构,公众号文章推送的架构文章还不多吗?),按照百度或者Google出来的套路,参考别人的经验,即使没有丰富的架构实战经验,也可以做出基本可行的架构。

识别复杂度

根据前面的原则设计适合当下的系统,千万不要过于复杂。给各个子系统,组件作复杂度排序,优先解决复杂度最高的问题。对于同一个复杂度的问题,软件系统的方案可以有多个,总是可以综合挑出个性价比最高的方案。

设计备选方案

确定了系统面临的主要问题后,方案设计就有了明确目标,我们就可以开始真正进行架构设计了。绝大部分的架构设计不需要天才那样的创新,首先,对一存在的技术和开源工具组件非常熟悉,对已经验证过的架构模式烂熟于心,然后根据自己对业务的理解,挑选合适的架构模式进行组合,再对组合后的方案进行修改和调整。其实本质上跟电脑城配置组装电脑的没啥区别,你要根据当前的成本,了解各种CPU,内存,主板的性价比。

当然,架构设计需要有些创新,不过也就是微创新,大部分情况都是基于已有的成熟技术和架构。不过话说回来,也不要真把架构设计当成电脑城组装电脑的人员了。

根据这章节的标题,想说,架构设计的一个错误就是只做一个方案。

这样做有很多弊端,一个是万一经验不足,想的不够全面,架构评审的时候会出问题。

所以:

正确的做法是,备选阶段关注的是技术选型,而不是技术细节,技术选型的差异要比较明显。比如Zookeeper和etcd这两种方案差异就非常大。

评估和选择备选方案

选择备选方案有点困难,不过有以下一些原则,这个得看场景,至于怎么选,凭借经验了:

怎样选择以上方案呢?大概套路是这样:

详细方案设计

主要就是选择的方案的技术细节了,需要对最终选择的方案的关键细节有比较深入的理解,比如用ES做全文搜索方案,那么需要对ES的设计原理有深入的理解,比如索引,副本,集群等。不能道听途说,看几篇文章的出结论。当然,你要深入理解,肯定要查找比较多的文章资料的,这一步是必不可少的,还有自己亲手搭建方案环境验证一些原型设计。

结语

创世记的宏观概念和基本法差不多就两篇文章说完了,可能之后的架构文章就是比较细节的技术方面的研究了,这样更有助于宏观的选型。

EOF