写点什么

面向复杂度架构设计

用户头像
郑印
关注
发布于: 刚刚

什么是面向复杂度架构设计

面向复杂度架构设计是由李运华老师总结提出的,架构设计的本质是降低系统软件系统的复杂度,只有通过架构设计将系统的整体复杂度降低、分解、才能通过多人一起协同完成一个复杂系统的功能。


那么系统有哪些复杂度呢?


  1. 业务复杂度


业务复杂度指的就是业务本身所产生的复杂度,包括业务中的角色、行为以及它们之间的关系。比如一个简单的个人博客,业务复杂度就很低,但是如果是做一个写作平台,比如微信公众号,业务的复杂度就很高。


  1. 质量复杂度


质量复杂度指的是软件系统高性能、高可用、高扩展、成本、安全等条件的约束。


架构设计就是通过识别到这些复杂度的来源,然后设计应对的解决方案,比如在应对业务复杂度时可以使用一些 DDD 的设计模式,在应对技术复杂度时可以使用一些缓存、数据分片等解决方案。


最后我们来总结一下什么是面向复杂度的架构


  • 本质,降低软件系统的复杂度

  • 思路,通过分析系统需求找到系统复杂的地方,然后设计方案

  • 模式,复杂度来源:高性能、高可用、可扩展、安全、成本……

  • 套路,分库分表、缓存、集群、分片、微服务、DDD、异地多活……

4R 架构

  • Rank , 架构是分层的 ;

  • Role , 系统包含哪些角色;

  • Relation , 角色之间的关系;

  • Rule ,角色如何协作完成系统功能;


Role、Relation、Rule 在业务角度是在描述业务知识,假如说我们现在希望把一个拍卖会搬到线上,那么这里面的 Role 就有拍主、买家、拍卖公司,Relation 就是拍卖公司是拍主与买家的中间,Rule 则是具体的拍卖方式,根据这些我们可以一个服务内部的架构,比如通过 DDD 战术设计的方式。​


Role、Relation、Rule 在系统角度是在描述系统之间的运作规则,还是上面的例子,拍卖会搬到线上,那么这里面按照业务的边界划分,会有一个上拍的服务(委托给拍卖公司的拍品以及佣金的合同),竞价的服务(买家对拍品进行出价的服务,包括一系列的出价规则),交易服务(买家胜出后后续与交易流程),此时各服务就是 Role, 竞价服务需要依赖于商品服务才能进行出价,交易服务需要依赖于竞价服务,不然不知道那些拍品应该成交,此时描述的是它们的 Relation 与 Rule。​


Rank 所描述的是架构从自顶向下是分层的,比如微服务架构与具体的微服务实现的架构在到具体的微服务接口限流架构。

架构设计原则

合适原则

合适原则宣言:“合适优于业界领先”!


在我们做架构选型时,会面临各种技术方案,选择最合适我们的,而不是最领先于业界的。

简单原则

简单原则宣言:“简单优于复杂”


如果能在一个系统内搞定的事情,就不要引入第二个的系统。

演化原则

演化原则宣言:“演化优于一步到位”


没有一步到位的架构,在 UV 只有十万级别时就不要去考虑千万级别的事情,即使 UV 有百万级别,那也分是一百万,还是 9 百万,不要过度设计架构,而是在演化中不断的完善与升级架构。

架构设计原则的应用

  1. 设计出来的架构要满足当时的业务需要,符合团队和技术的能力水平(合适原则)

  2. 先按照简单的方式来设计架构,然后不断地在实际应用过程中迭代优化(简单原则)

  3. 当业务发生变化时,架构要扩展、重构,甚至重写(演化原则)

复杂度模型

业务复杂度

业务固有的复杂度,主要体现为难以理解、难以扩展,例如业务数量多(微信)、业务流程长(支付宝)、业务之间关系复杂(例如 ERP)。

质量复杂度

高性能、高可用、成本、安全等质量属性的要求。

复杂度的应对之道

可扩展复杂度模型

可扩展分为,可理解与可复用两个部分,通过拆分来提高可理解性,通过封装来提高复用性


  • 架构可扩展

  • 可理解性; 架构分层,服务拆分

  • 应用/代码可扩展

  • 可理解性;拆分 module,package,class

  • 可复用性;通过面向对象、设计模式、规则引擎进行封装

拆分的复杂度模型

  • 拆分第一原则 - 内外平衡原则


内部复杂度和外部复杂度是天平的两端,一方降低,另一方必然升高,关键在于平衡


  • 拆分第二原则 - 先粗后细原则


如果你把握不准,那么就先拆少一些,后面发现有问题再继续拆分。

封装的复杂度模型

封装来自于对于变化的预测,只有先预测变化才能决定如何封装变化。​


比如在一个基于 ES 的搜索系统中,根据 ES 的查询结构体,那么它是有限的,比如 function_score 、聚合、高亮等,同时对于一个搜索系统它的查询字段通常也是有限的,比如一个图书系统的查询字段无外乎商品名、作者、出版社、分类、介绍等等... 虽然很多,但是在确定后变化的可能性不大。而在搜索系统中 ES 查询结构体里的内部参数是可能变化的,字段的构建策略也是可能变化的,那面可以将不变的区块抽象为类,将变化的部分抽象为类行为,并结合设计模式以此来达到封装的目的。​

预测的方法

  1. 只预测 2 年内的可能变化,不要试图预测 10 年后的变化

  2. 预测没有把握就不要封装,等到需要的时候重构即可

封装的方法

  1. 面向对象

  2. 设计模型

  3. DDD


高性能复杂度模型

单机的复杂度模型


计算的高性能



存储的高性能


集群的复杂度模型

集群的计算高性能与存储高性能可以通过,任务分配与任务分解来完成

任务分配与任务分解

  • 任务分配是指将一系列的任务,根据任务的数量,分配到多个处理方进行处理。调度器接收到请求,按照轮询、hash、随机等算法对流量进行划分,将划分后的流量直接交由后端节点处理,这个过程不涉及对流量类型的鉴别。



  • 任务分解是指将一系列的任务,根据任务的类型,分配给多个处理方进行处理。调度器收到请求,通过实现的路由规则、任务的复杂度、后端服务器的处理能力等因素,将请求分配到合适的后端处理节点进行处理。



任务分配




任务分配的实现举例:​



任务分解




任务分解的实现举例:​



在一些调度器上,任务分配与分解是相互配合的,比如通过 Nginx 调度时,通过转发规则配置 proxy,这个是任务分解,但是在具体的 proxy 转发时又可以通过 hash 的方式进行任务分配;但在有些调度器上,也可能仅存在一种方式,比如 yarn 进行资源调度时,它通过资源的利用率,以及任务的复杂度判断交由那些后端节点执行,此时不存在任务的分配场景。


高可用的复杂度模型



从理论上讲系统是没有 100%可用的,所谓高可用是不断的提高系统对于故障的容错能力,如果系统可用做到 4 个 9,那么表示该系统在连续运行 1 年时间里最多可能的业务中断时间是 52.6 分钟。


3 个 9 的宕机时间计算: (1-99.9%)36524=8.76 小时 4 个 9 的宕机时间计算: (1-99.99%)36524=0.876 小时=52.6 分钟 5 个 9 的宕机时间计算: (1-99.999%)36524*60=5.26 分钟

计算的高可用

计算的高可用与高性能一样,采样任务分配与任务分解的方式进行,不同的是计算高可用需要判断后端节点是否存活,因此需要定期的做健康检测与服务的上线下线。

存储的高可用

想要保证存储的高可用首先需要做的就是数据的多副本,因此存储高可用涉及最多的就是数据复制、共识算法相关的技术内容。​

数据复制的格式

  • 命令 , 当主节点执行一个命令时,将命令复制到从节点在从节点在次执行,以此来保持数据的一致性。这种方式通常用于增量复制,比如 Redis 和 Mysql 基于 sql 的复制方式(STATEMENT)。

  • 数据,当主节点执行一个命令后,讲受影响的数据与操作发送到从节点,从节点根据收到的数据与操作更新数据,以此保持数据的一致性。这种方式通常应用增量复制,比如 Mysql 基于行的复制方式(ROW)

  • 文件,通过在主从服务直接拷贝数据文件的方式进行传递,这种方式通常适用于首次的全量复制。

数据复制的方式

  • 同步复制

  • 写主时,同时写从,两边都写完后才返回,一致性最强但性能差,通常用于主从系统。

  • 异步复制

  • 写主后,有一个线程或进程去同步到从,性能好但容易出现数据不一致,通常用于主从系统。

  • 半同步复制

  • 写主后,一个从节点采用同步复制,其余从节点采用异步复制,综合两者的优缺点,通常用于一主多从的系统。

  • 多数同步复制

  • 写主后,多数节点采用同步复制,其余采用异步复制。数据一致性强、性能差,通常用于分布式存储系统。

数据状态决策

数据状态决策是指通过何种方法判断数据的可用性,从新决定是否对外提供服务。


  • 独裁式

  • 独裁式是指由一个中心服务作为决策者,中心服务于各数据节点进行通信,采集信息,最终判断哪些节点可以对外提供服务。独裁式需要保障决策者的自身的单点问题,因此通常会借用成熟的软件来作为独决策者,比如 Hdfs 借助 Zookeeper 作为决策者。

  • 协商式

  • 协商式通常用于主备架构中,相互之间建立心跳机制,当以此判断谁可以作为主节点提供服务。

  • 民主式

  • 目前大多数分布式的系统都采样民主式进行决策,民主式的算法很多,列如 Raft,ZAB,Paxos 等。民主式的实现复杂,好在目前已经有很多开源的实现。

  • 民主式在决策时,可能出现脑裂问题,当出现网络分区时,集群被分为了两个部分,就好像人群被分为两拨单独进行投票选举,这样就会产生两个 Leader,最终产生的问题就是集群的数据不一致。

  • quorum 机制用于解决脑裂问题

架构质量

低成本

高性能、高可用、高扩展实际上都是对低成本的约束,这些约束体现在两个方面对成本的增加。


  1. 硬件成本的增加


要实现上面这些特性必然会增加冗余,冗余就带来硬件成本的提高。


  1. 人工成本的增加


要实现 3 高特性,必然将增加系统的复杂度,这些复杂度都需要人工去消化,比如自研某个功能组件。

低成本的方案

  • 通过优化系统的方式来降低系统的硬件成本,比如采用更高效的算法,对应用进行调优,引入一些缓存中间件来减少数据库的技术压力等。

  • 通过采用开源而非自研的方式来降低人工成本。

  • 关注技术发展,采用更先进的软件对原本系统进行升级、替换。

注意事项

  1. 先设计架构,在看如何降低成本!

  2. 宁花机器一分,不花程序员一秒!

安全性

安全性体现在两个部分,架构安全与业务安全。​


架构安全用于防止一些可预期的强盗行为,比如流量攻击、无规矩的爬虫等,通常采样防火墙、流量清洗、限流等方式预防。​


业务安全是业务开发过程中导致的安全问题,比如业务漏洞,SQL 注入、越权等。常见的就是业务的因为漏洞被羊毛党薅羊毛。

可测试性

可测试性是指,软件系统在测试环境下能否方便的支持测试各种场景的能力。


软件系统通常都有一些的常规的测试方法,比如单元测试、集成测试、接口测试、自动化测试,这些常规的测试方法通常实在正常的分支下,对系统的功能进行测试。然而系统总归有异常的时候,如何测试异常时系统的表现,体现了系统可测试性的能力。​


  1. 当系统在应对大量流量下的应对能力测试。 (压力测试、链路压测)

  2. 当系统依赖组件故障是系统的应对能力。(故障测试)

  3. 当系统在某些业务异常分支下的表现情况。(异常数据与异常配置开关)



因此在这些测试场景下,系统应该能够可以有能力的去模拟出这些情况,这样系统才满足了更深层的可测试性要求。​

可维护性

可维护性是指,软件系统支持定位问题、修复问题的能力。​


软件系统在运行过程中可能会出现一些突发的应对情况,或者在日常维护工程中也需要对运行中的软件进行一些手动的干预。那么如何能够让外部可以干预软件系统的运行过程呢,这就是可维护性要做的工作,比如对某些限流的阈值进行修改,调整一下日志的级别排查一些问题,亦或者通过外部配置开关控制内部功能的打开与关闭等。

可观测性

可观测性是指,软件系统对外展现内部状态的能力。​



可观测性目前是个热门话题,上图描述的是可观测性的三板斧,它们每个解决观测的粒度都有所不同,相互配合完成系统的可观测工作。​


详细可以参见,我写的可观测性架构实践系列文章

推荐

面向复杂度设计,这是一套非常具有逻辑性的,帮助我们做架构决策的方法。推荐李运华老师架构课程,链接 : https://u.geekbang.org/subject/arch2nd

用户头像

郑印

关注

还未添加个人签名 2017.10.17 加入

语雀 , https://www.yuque.com/izhengyin

评论

发布
暂无评论
面向复杂度架构设计