如何设计可扩展架构笔记

170 阅读7分钟
  1. 理解架构设计复杂度模型
  2. 理解可扩展架构的复杂度本质
  3. 掌握可扩展架构的“拆分”和“封装”手段

一、架构设计复杂度模型

1.1 业务复杂度

业务固有的复杂度,主要体现为难以理解、难以扩展,例如

  1. 业务数量多(微信),公众号、朋友圈、支付等业务数量多
  2. 业务流程长(支付宝),完成支付经过的流程特别多,用户、商家、内部系统收单、收银、支付风控、核算,外部跟网银、网联、银行打交道,支付流程会特别长
  3. 业务之间关系复杂(ERP),to B的业务都很复杂

1.2 质量复杂度

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

  1. 典型的就是高性能、高可用,每秒支持1千个请求、1万个请求、10万个请求它的系统设计是完全不一样的
  2. F5、LVS、Nginx做负载均衡,同样都是负载均衡,它们的性能量级会差异很大,就会导致系统的架构会差异很大

1.3 架构复杂度模型

业务复杂度从本质上来说,它和质量复杂度是正交的。业务复杂度本身不会影响质量复杂度,同样质量复杂度也不会影响业务复杂度,就会出现如下分区

image.png

  1. 有的业务复杂度高,质量复杂度不高,比如ERP、业务流系统、工作流系统

  2. 有的业务复杂度不高,质量复杂度高,比如常见的中间件系统,如:Nginx、Reids、MySQL。

    Redis功能简单就是一个数据结构的存储,key/value的存储,它的接口也比较少,但是它的指令复杂度很高,因为它要做到很高的TPS。Redis单进程后才能做到5万到10万的TPS

  3. 双高的业务或系统,比如:支付宝、淘宝、电信系统、微信、金融系统,还有一个最高的OS

  4. 双低的系统,比如创业初期的系统、后台管理系统和运营管理系统

这个架构复杂度模型能够帮我们快速地梳理出我们的系统的复杂度到底有可能落在哪个区间的,不同复杂度区间的应对方式是不同的。复杂度越高,架构设计越难!

1.4 架构复杂度应对之道

image.png

  1. 双低,框架,如LAMP、SSH、Ruby on RAils,开发效率最关键
  2. 业务复杂度高,质量复杂度低,SOA、DDD、微服务,
  3. 业务复杂度低,质量复杂度高,集群、缓存、Reactor、分片、副本,高性能、高可用、异地多活、主从复制、选举等
  4. 高高,融合所有模式

DDD不能降低质量复杂度,只能降低业务复杂度

1.5 架构设计环

image.png

二、可扩展复杂度模型

2.1 可扩展定义

有两个类似的概念

  1. 可扩展,extensibility,系统适应变化的能力,包含可理解和可复用两部分
  2. 可伸缩,scalability,系统通过添加更多资源来提升性能的能力

可理解和可复用是如何影响可扩展的?

  1. 可理解,假设系统很难理解,要做一个需求,不知道这个需求会影响哪些地方或不知道有哪些地方要改,扩展起来就很麻烦;
  2. 可复用,如果说来了一个新的需求,你知道要改哪些东西但是要改的地方特别多,可扩展同样会有问题,扩展的效率会很低;

2.2 可扩展复杂度模型

image.png

  1. 架构可扩展,第一种拆分(微服务),拆分形态拆分成一个个的服务,拆分粒度变是拆分成多少个微服务

  2. 架构可扩展,第二种拆分(分层),MVC 微服务跟分层都是从架构层面去将系统进行拆分,来降低系统难以理解的复杂度

  3. 应用可扩展,第一种拆分,拆分成模块、package、class

  4. 代码可扩展,

DDD分为战略设计和战术设计,同时涵盖了可理解和可复用。

三、可扩展的架构设计 - 拆分

3.1 鸡蛋篮子理论

image.png

  1. 可扩展、高性能、高可用其实都是鸡蛋篮子理论,但是是不同法则
  2. 拆分法则,如果一个篮子数不清,拆分到多个篮子再数!比如数2万块钱,分成几堆多个人数

3.2 拆分复杂度模型

image.png 什么叫拆分的形态,比如说你把系统拆分成服务,比如学生管理系统,拆分成学生服务、课程服务、权限服务;第二种形态就是拆分成模块,在编码的时候

拆分粒度和拆分形态不是排列组合关系,而是约束关系

3.3 拆分粒度 - 两个复杂度

内部复杂度,又称为单体复杂度,指单个对象内部的复杂度,例如传统的单体系统,所以业务都在一个系统里面。可以用参与的开发人数来衡量单个拆分对象的复杂度。例如:3个人负责一个子系统/子模块是比较合理的,20个人在同一个子系统上开发,则内部复杂度过高。

外部复杂度,双称为群体复杂度,指拆分后的多个对象之间的关系复杂度。可以用业务流程涉及对象数量来衡量外部复杂度。例如:一次用户请求需要5个子系统参与是比较合理的,如果需要20个子系统参与,则外部复杂度过高。

哪类复杂度问题更多? 一般情况下其实是外部复杂度问题更多,把系统拆分的太细,它带来的问题实际上比你系统不拆分带来的问题更多,因为系统拆分后,子系统之间的关联是指数级增长的,它的接口更多,交互更多,相互之间的依赖关系更复杂,所以外部复杂度带来的问题比内部复杂度带来的问题更多。

3.4 拆分粒度 - 平衡的艺术

  1. 拆分第一原则:内外平衡原则,内部复杂度和外部复杂度是天平的两端,一方降低,另一方必然升高,关键在于平衡。
  2. 拆分第二原则:先粗后细原则,如果把握不准,那么就先拆少一些,后面发现有问题再继续拆分。

为什么不先拆多一些,然后合并? 外部复杂度带来的问题比内部复杂度带来的问题更多,先拆细问题非常多,把它合并起来合并的工作量比拆分的工作量要多很多的。先拆的很细,对外提供了很多接口,合并要把接口取消,但是取消这些在线上运行的接口比新增接口要复杂的多,所以拆分的时候要先分后细

四、可扩展的架构设计 - 封装

4.1 封装复杂度模型

image.png

  1. 预测变化,时间跨度、变化方式决定封装模型
  2. 封装变化
  3. 预测变化的方式会决定封装模型如何设计

4.2 预测的艺术

  1. 预测最大的挑战:一切皆有可能?
  2. 预测第一原则:2年原则。只预测2年内的可能变化,不要试图预测10年后的变化。例如:你准备接入微信支付,那么预测接入支付宝就是很自然的,但数字钱包就没有那么必要了
  3. 预测第二原则:3次法则。预测没有把握就不要封装,等到需要的时候重构即可。例如:要不要支持数据库为Oracle、MySQL、PostgreSQL?

(Martin Fowler)Rule of three: Three strikes and you refactor(1写2抄3封装,再一再二不再三,再三再四没意思)

4.3 封装的技巧

image.png

我们可以用如下封装模型来封装变化

  1. 规则引擎
  2. 微内核
  3. 抽象层
  4. 设计模式