架构设计

架构设计的主要目的是为了解决软件系统复杂度带来的问题

架构分析

高性能

  • 单机复杂度 如果我们要完成一个高性能的软件系统,需要考虑如多进程、多线程、进程间通信、多线程并发等技术点,而且这些技术并不是最新的就是最好的,也不是非此即彼的选择。在做架构设计的时候,需要花费很大的精力来结合业务进行分析、判断、选择、组合,这个过程同样很复杂。

  • 集群的复杂度

    1. 任务分配 任务分配的意思是指每台机器都可以处理完整的业务任务,不同的任务分配到不同的机器上执行。
    2. 通过任务分解的方式,能够把原来大一统但复杂的业务系统,拆分成小而简单但需要多个系统配合的业务系统。

高可用

系统的高可用方案五花八门,但万变不离其宗,本质上都是通过“冗余”来实现高可用。

  • 计算高可用 这里的“计算”指的是业务的逻辑处理。计算有一个特点就是无论在哪台机器上进行计算,同样的算法和输入数据,产出的结果都是一样的,所以将计算从一台机器迁移到另外一台机器,对业务并没有什么影响。

  • 存储高可用 对于需要存储数据的系统来说,整个系统的高可用设计关键点和难点就在于“存储高可用”。存储与计算相比,有一个本质上的区别:将数据从一台机器搬到到另一台机器,需要经过线路进行传输。

  • 高可用状态决策 无论是计算高可用还是存储高可用,其基础都是“状态决策”,即系统需要能够判断当前的状态是正常还是异常,如果出现了异常就要采取行动来保证高可用。如果状态决策本身都是有错误或者有偏差的,那么后续的任何行动和处理无论多么完美也都没有意义和价值。但在具体实践的过程中,恰好存在一个本质的矛盾:通过冗余来实现的高可用系统,状态决策本质上就不可能做到完全正确。

    1. 独裁式:独裁式决策指的是存在一个独立的决策主体,我们姑且称它为“决策者”,负责收集信息然后进行决策;所有冗余的个体,我们姑且称它为“上报者”,都将状态信息发送给决策者。
    2. 协商式:协商式决策指的是两个独立的个体通过交流信息,然后根据规则进行决策,最常用的协商式决策就是主备决策。
    3. 民主式:民主式决策指的是多个独立的个体通过投票的方式来进行状态决策。例如,ZooKeeper 集群在选举 leader 时就是采用这种方式。

可扩展性

设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化。

  • 预测变化

    • 不能每个设计点都考虑可扩展性。
    • 不能完全不考虑可扩展性。
    • 所有的预测都存在出错的可能性。
  • 应对变化

    1. 将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”。
    2. 提炼出一个“抽象层”和一个“实现层”。

低成本

往往只有“创新”才能达到低成本目标。

无论是引入新技术,还是自己创造新技术,都是一件复杂的事情。引入新技术的主要复杂度在于需要去熟悉新技术,并且将新技术与已有技术结合起来;创造新技术的主要复杂度在于需要自己去创造全新的理念和技术,并且新技术跟旧技术相比,需要有质的飞跃。相比来说,创造新技术复杂度更高,因此一般中小公司基本都是靠引入新技术来达到低成本的目标;而大公司更有可能自己去创造新的技术来达到低成本的目标,因为大公司才有足够的资源、技术和时间去创造新技术。

安全

从技术的角度来讲,安全可以分为两类:一类是功能上的安全,一类是架构上的安全。

  1. 功能安全 功能安全其实就是“防小偷”。常见的 XSS 攻击、CSRF 攻击、SQL 注入、Windows 漏洞、密码破解等,本质上是因为系统实现有漏洞,黑客有了可乘之机。

  2. 架构安全 架构安全就是“防强盗”。传统的架构安全主要依靠防火墙,防火墙最基本的功能就是隔离网络,通过将网络划分成不同的区域,制定出不同区域之间的访问控制策略来控制不同信任程度区域间传送的数据流。

规模

规模带来复杂度的主要原因就是“量变引起质变”,当数量超过一定的阈值后,复杂度会发生质的变化。

  1. 功能越来越多,导致系统复杂度指数级上升
  2. 数据越来越多,系统复杂度发生质变

设计原则

业务千变万化,技术层出不穷,设计理念也是百花齐放,看起来似乎很难有一套通用的规范来适用所有的架构设计场景。但是在研究了架构设计的发展历史、多个公司的架构发展过程(QQ、淘宝、Facebook 等)、众多的互联网公司架构设计后,我发现有几个共性的原则隐含其中,这就是:合适原则、简单原则、演化原则,架构设计时遵循这几个原则,有助于你做出最好的选择。

合适原则

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

  1. 没那么多人,却想干那么多活,是失败的第一个主要原因。
  2. 没有那么多积累,却想一步登天,是失败的第二个主要原因。
  3. 没有那么卓越的业务场景,却幻想灵光一闪成为天才,是失败的第三个主要原因。

简单原则

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

软件领域的复杂性体现在两个方面:

  1. 结构的复杂性 结构复杂的系统几乎毫无例外具备两个特点:
  • 组成复杂系统的组件数量更多;
  • 同时这些组件之间的关系也更加复杂。
  1. 逻辑的复杂性 功能复杂的组件,另外一个典型特征就是采用了复杂的算法。复杂算法导致的问题主要是难以理解,进而导致难以实现、难以修改,并且出了问题难以快速解决。

演化原则

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

考虑到软件架构需要根据业务发展不断变化这个本质特点,软件架构设计其实更加类似于大自然“设计”一个生物,通过演化让生物适应环境,逐步变得更加强大

软件架构设计同样是类似的过程:

  • 首先,设计出来的架构要满足当时的业务需要。
  • 其次,架构要不断地在实际应用过程中迭代,保留优秀的设计,修复有缺陷的设计,改正错误的设计,去掉无用的设计,使得架构逐渐完善。
  • 第三,当业务发生变化时,架构要扩展、重构,甚至重写;代码也许会重写,但有价值的经验、教训、逻辑、设计等(类似生物体内的基因)却可以在新架构中延续。

架构师在进行架构设计时需要牢记这个原则,时刻提醒自己不要贪大求全,或者盲目照搬大公司的做法。应该认真分析当前业务的特点,明确业务面临的主要问题,设计合理的架构,快速落地以满足业务需要,然后在运行过程中不断完善架构,不断随着业务演化架构。

即使是大公司的团队,在设计一个新系统的架构时,也需要遵循演化的原则,而不应该认为团队人员多、资源多,不管什么系统上来就要一步到位,因为业务的发展和变化是很快的,不管多牛的团队,也不可能完美预测所有的业务发展和变化路径。

设计流程

识别复杂度

只有正确分析出了系统的复杂性,后续的架构设计方案才不会偏离方向;否则,如果对系统的复杂性判断错误,即使后续的架构设计方案再完美再先进,都是南辕北辙,做的越好,错的越多、越离谱。

将主要的复杂度问题列出来,然后根据业务、技术、团队等综合情况进行排序,优先解决当前面临的最主要的复杂度问题。

设计备选方案

架构师的工作并不神秘,成熟的架构师需要对已经存在的技术非常熟悉,对已经经过验证的架构模式烂熟于心,然后根据自己对业务的理解,挑选合适的架构模式进行组合,再对组合后的方案进行修改和调整。

  • 第一种常见的错误:设计最优秀的方案。
  • 第二种常见的错误:只做一个方案。
    • 备选方案的数量以 3 ~ 5 个为最佳。
    • 备选方案的差异要比较明显。
    • 备选方案的技术不要只局限于已经熟悉的技术。
  • 第三种常见的错误:备选方案过于详细。

评估和选择备选方案

列出我们需要关注的质量属性点,然后分别从这些质量属性的维度去评估每个方案,再综合挑选适合当时情况的最优方案。

常见的方案质量属性点有:性能、可用性、硬件成本、项目投入、复杂度、安全性、可扩展性等。在评估这些质量属性时,需要遵循架构设计原则 1“合适原则”和原则 2“简单原则”,避免贪大求全,基本上某个质量属性能够满足一定时期内业务发展就可以了。

备选方案的选择和很多因素相关,并不单单考虑性能高低、技术是否优越这些纯技术因素。业务的需求特点、运维团队的经验、已有的技术体系、团队人员的技术水平都会影响备选方案的选择。

详细方案设计

简单来说,详细方案设计就是将方案涉及的关键技术细节给确定下来。

详细设计方案阶段可能遇到的一种极端情况就是在详细设计阶段发现备选方案不可行,一般情况下主要的原因是备选方案设计时遗漏了某个关键技术点或者关键的质量属性。

  • 架构师不但要进行备选方案设计和选型,还需要对备选方案的关键细节有较深入的理解。
  • 通过分步骤、分阶段、分系统等方式,尽量降低方案复杂度
  • 如果方案本身就很复杂,那就采取设计团队的方式来进行设计,博采众长

高性能模式

数据库读写分离

数据库分库分表

高性能NoSQL

高性能缓存架构

高性能负载均衡

高可用模式

接口故障

降级、熔断、限流和排队

可扩展模式

可扩展性架构的设计方法很多,但万变不离其宗,所有的可扩展性架构设计,背后的基本思想都可以总结为一个字:拆!

拆,就是将原本大一统的系统拆分成多个规模小的部分,扩展时只修改其中一部分即可,无须整个系统到处都改,通过这种方式来减少改动范围,降低改动风险。

技术演进

技术创新推动业务发展!

平台技术

运维平台的核心设计要素是“四化”:标准化、平台化、自动化、可视化。

架构模式

复杂的软件必须有清晰合理的架构,否则无法开发和维护。

MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用。它本身很容易理解,但是要讲清楚,它与衍生的 MVP 和 MVVM 架构的区别就不容易了。

MVC

  • 视图(View):用户界面。
  • 控制器(Controller):业务逻辑
  • 模型(Model):数据保存

MVP

  • 各部分之间的通信,都是双向的。
  • View 与 Model 不发生联系,都通过 Presenter 传递。
  • View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。

MVVM

  • 基本上与 MVP 模式完全一致
  • 它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。