跳到主要内容

架构设计

Create by fall on 03 Feb 2022 Recently revised in 27 Apr 2023

架构可视化工具 Tequila coca arch

得到项目的调用关系图,反映出系统的架构。可以得知:

  • 项目的结构化分是否合理
  • 查看项目中的代码是否存在循环依赖

Plug-in Architecture

插件化架构

是一种面向功能进行拆分的可扩展性架构,通常用于实现基于产品的应用。插件化架构模式允许你将其他应用程序功能作为插件添加到核心应用程序,从而提供可扩展性以及功能分离和隔离。

包括两种类型的架构组件:核心系统(Core System)和插件模块(Plug-in modules)

Core System 的功能相对稳定,不会因为业务功能扩展而不断修改,而插件模块是可以根据实际业务功能的需要不断地调整或扩展。

通常插件模块之间也是独立的,也有一些插件是依赖于若干其它插件的。重要的是,尽量减少插件之间的通信以避免依赖的问题。

  • 灵活性高:整体灵活性是对环境变化快速响应的能力。由于插件之间的低耦合,改变通常是隔离的,可以快速实现。通常,核心系统是稳定且快速的,具有一定的健壮性,几乎不需要修改。
  • 可测试性:插件可以独立测试,也很容易被模拟,不需修改核心系统就可以演示或构建新特性的原型。
  • 性能高:虽然插件化架构本身不会使应用高性能,但通常使用插件化架构构建的应用性能都还不错,因为可以自定义或者裁剪掉不需要的功能。

加载插件的时机

核心系统提供插件注册表(可以是配置文件,也可以是代码,还可以是数据库)

比如 package.json 中声明一些 options 的可选

{
"peerDependenciesMeta": {
"sass": {
"optional": true
},
"terser": {
"optional": true
}
}
}

插件注册表含有每个插件模块的信息,包括它的名字、位置、加载时机(启动就加载,或是按需加载)等。

插件通信

插件之间没有直接联系,所以只能通过核心系统进行联系,可以使用了 发布订阅模式

架构设计原则

GRASP

软件设计原则 GRASP 通用职责分配软件模式

低耦合(Low Coupling)

赋予职责使得对象间的耦合度尽可能低,最小化对象间的依赖和变更影响,最大化重用。

重复书写的内容尽可能低,负责的内容各自分隔。

高内聚(High Cohesion)

赋予职责使得每个对象的职责尽可能保持聚焦和单一,易于管理和理解。

控制器 (Controller)

把职责赋予系统、设备或者子系统的表示类 (门面控制器),或者某个用例的表示类 (用例控制器),让控制器接收事件并协调整个系统的运作。

多态 (Polymorphism)

将职责分配给多个具有同名方法的多态子类,运行时根据需要动态切换子类,让系统行为变得可插拔。

纯虚构 (Pure Fabrication)

针对真实问题域中不存在,但是设计建模中有用的概念,设计虚构类并赋予职责。

间接 (Indirection)

在两个或者多个对象间有交互的情况下,为避免直接耦合,提高重用性,创建中间类并赋予职责,对象的交互交由中间类协调。

受保护的变化 (Protected Variation)

简单讲就是封装变化。识别系统中可能的不稳定或者变化,在不稳定组件上创建稳定的抽象接口,将可能的变化封装在接口之后,使得系统内部的不稳定或者变化不会对系统的其它部分产生不良影响。

SOLID 面向对象设计原则

S.O.L.I.D 是面向对象设计和编程 (OOD&OOP) 中几个重要原则的首字母缩写,受 Robert Martin 推崇。

SOLID

面向对象设计原则

单一职责原则 (The Single Responsibility Principle)

修改某个类的理由应该只有一个,如果超过一个,说明类承担不止一个职责,要视情况拆分。

开放封闭原则 (The Open Closed Principle)

软件实体应该对扩展开放,对修改封闭。一般不要直接修改类库源码(即使你有源代码),通过继承等方式扩展。

里氏替代原则 (The Liskov Substitution Principle)

当一个子类的实例能够被替换成任何超类的实例时,它们之间才是真正的 is-a 关系。

依赖倒置原则 (The Dependency Inversion Principle)

高层模块不应该依赖于底层模块,二者都应该依赖于抽象。换句话说,依赖于抽象,不要依赖于具体实现。比方说,你不会把电器电源线焊死在室内电源接口处,而是用标准的插头插在标准的插座 (抽象) 上。

接口分离原则 (The Interface Segregation Principle)

不要强迫用户去依赖它们不使用的接口。换句话说,使用多个专门的接口比使用单一的大而全接口要好。

AKF

分布式系统架构设计原则和理论 AKF 架构原则

N + 1 设计

永远不要少于两个,通常为三个。比方说无状态的 Web/API 一般部署至少>=2 个。

回滚设计

确保系统可以回滚到以前发布过的任何版本。可以通过发布系统保留历史版本,或者代码中引入动态开关切换机制 (Feature Switch)。

禁用设计

能够关闭任何发布的功能。新功能隐藏在动态开关机制 (Feature Switch) 后面,可以按需一键打开,如发现问题随时关闭禁用。

监控设计

在设计阶段就必须考虑监控,而不是在实施完毕之后补充。例如在需求阶段就要考虑关键指标监控项,这就是度量驱动开发 (Metrics Driven Development) 的理念。

设计多活数据中心

不要被一个数据中心的解决方案把自己限制住。当然也要考虑成本和公司规模发展阶段。

使用成熟的技术

只用确实好用的技术。商业组织毕竟不是研究机构,技术要落地实用,成熟的技术一般坑都被踩平了,新技术在完全成熟前一般需要踩坑躺坑。

异步设计

能异步尽量用异步,只有当绝对必要或者无法异步时,才使用同步调用。

无状态系统

尽可能无状态,只有当业务确实需要,才使用状态。无状态系统易于扩展,有状态系统不易扩展且状态复杂时更易出错。

水平扩展而非垂直升级

永远不要依赖更大、更快的系统。一般公司成长到一定阶段普遍经历过买更大、更快系统的阶段,即使淘宝当年也买小型机扛流量,后来扛不住才体会这样做不 scalable,所以才有后来的去 IOE 行动。

设计时至少要有两步前瞻性

在扩展性问题发生前考虑好下一步的行动计划。架构师的价值就体现在这里,架构设计对于流量的增长要有提前量。

非核心则购买

如果不是你最擅长,也提供不了差异化的竞争优势则直接购买。避免 Not Invented Here 症状,避免凡事都要重造轮子,毕竟达成业务目标才是重点。

使用商品化硬件

在大多数情况下,便宜的就是最好的。这点和第 9 点是一致的,通过商品化硬件水平扩展,而不是买更大、更快的系统。

小构建、小发布和快试错

全部研发要小构建,不断迭代,让系统不断成长。这个和微服务理念一致。

隔离故障

实现故障隔离设计,通过断路保护避免故障传播和交叉影响。通过舱壁泳道等机制隔离失败单元 (Failure Unit),一个单元的失败不至影响其它单元的正常工作。

自动化

设计和构建自动化的过程。如果机器可以做,就不要依赖于人。自动化是 DevOps 的基础。

要素应用

基准代码

一份基准代码,多份部署。如果用镜像部署方式,则一个镜像可以部署到多个环境 (测试,预发,生产),而不是给每个环境制作一个不同镜像。

依赖

显式声明依赖。如果用镜像部署,则一般依赖被直接打在镜像中,或者声明在 docker file 中。

配置

在环境中存储配置。在 Heroku 或者类似的 PaaS 平台上,配置一般是推荐注入到环境变量中的。现在采用集中式配置中心也是一种流行方式。

后端服务

把后端服务 (例如缓存,数据库,MQ 等) 当作附加资源,相关配置和连接字符串通过环境变量注入,或者采用配置中心。

构建、发布和运行

严格分离构建和运行。如果使用镜像部署,则构建、发布 / 运行是通过镜像这种中间格式严格分离的。

进程

一个或者多个无状态的进程运行应用。容器运行时相当于进程,适用于无状态 Web/API。

端口绑定

通过端口绑定提供服务。容器也是通过端口绑定对外提供服务。

并发

通过进程模型进行扩展。容器运行时相当于进程,通过起多个容器可以任意扩展并发数量。

易处理

快速启动和优雅终止可最大化健壮性。docker 容器支持秒级启动和关闭。

开发环境和线上环境等价

尽可能保持开发、测试、预发和线上环境相同。容器可以保证容器内运行时环境的一致性,还需要保证不同环境的一致性,例如不同环境内的操作系统,负载均衡,服务发现,后台服务,监控告警等要尽可能一致。

日志

把日志当作数据流。Heroku 不支持本地文件,所以必须以流方式把日志输送到后台日志服务。除了日志以外还要补充考虑 metrics 流的采集和输送。

管理进程

后台管理任务当作一次性的进程。其实相当于在 Heroku 上以独立进程方式运行任务 Job。

CAP 定理

2000 年 7 月,加州大学伯克利分校的 Eric Brewer 教授在 ACM PODC 会议上提出 CAP 猜想。2 年后,麻省理工学院的 Seth Gilbert 和 Nancy Lynch 从理论上证明了 CAP。之后,CAP 理论正式成为分布式计算领域的公认定理。

CAP 认为:一个分布式系统最多同时满足一致性 (Consistency),可用性 (Availability) 和分区容忍性 (Partition Tolerance) 这三项中的两项。

一致性 (Consistency)

一致性指“all nodes see the same data at the same time”,即更新操作成功,所有节点在同一时间的数据完全一致。

可用性 (Availability)

可用性指“Reads and writes always succeed”,即服务一直可用,而且响应时间正常。

分区容忍性 (Partition tolerance)

分区容忍性指“the system continue to operate despite arbitrary message loss or failure of part of the system.”,即分布式系统在遇到某节点或网络分区故障时,仍然能够对外提供满足一致性和可用性的服务。

BASE 理论

eBay 架构师 Dan Pritchett 基于对大规模分布式系统的实践总结,在 ACM 上发表文章提出了 BASE 理论,BASE 理论是对于 CAP 理论的延伸,核心思想是即使无法做到强一致性 (Strong Consistency,CAP 中的一致性指强一致性),但是可以采用适当的方式达到最终一致性 (Eventual Consistency)。

BASE 指基本可用 (Basically Available)、软状态 (Soft State) 和最终一致性 (Eventual Consistency)。

基本可用 (Basically Available)

基本可用是指分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。比如服务降级。

软状态 (Soft State)

软状态是指允许系统存在中间状态,而该中间状态不会影响系统的整体可用性。分布式存储中一般一份数据至少存三个副本,允许不同节点间副本同步的延迟就是软状态的体现。

最终一致性 (Eventual Consistency)

最终一致性是指系统中的所有数据副本经过一段时间后,最终能够达成一致状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

系统改进三原则

系统思考 (System Thinking)

开发驱动的组织,其能力不是制作软件,而是持续的交付客户价值。价值从业务需求开始,经过研发测试,到部署运维,依次流动,并最终以服务形式交付到客户手中。整个价值链流速并不依赖单个部分 (团队或个人) 的杰出工作,而是受整个价值链最薄弱环节 (瓶颈) 的限制。所以局部优化通常无效,反而招致全局受损。

Gene Kim 特别指出:Any improvements made anywhere besides the bottleneck are an illusion. 在瓶颈之外的任何优化提升都只是幻象。

强化反馈环 (Amplify Feedback Loops)

过程改进常常通过加强反馈环来达成。原理二强调企业和客户之间、组织团队间、流程上和系统内的反馈环。没有测量就没有提升,反馈要以测量数据为准,通过反馈数据优化改进系统。

持续试验和学习的文化 (Culture of Continual Experimentation And Learning)

在企业管理文化层面强调勇于试错和持续试验、学习和改进的文化。

参考文章

作者文章名称
阿宝哥从 13K 的前端开源项目我学到了啥?
yikejiucai架构师必须知道的架构设计原则