从零开始学架构-day03

2021-12-07 17:34:43 浏览数 (1)

架构设计的目的之一是为了解决项目的复杂度。复杂度的来源,追求高性能,高可用,并且拓展。先从高性能来看。

1. 高性能

1.什么是高性能?
  • 我们平时开发的时候曾经无数次有提到过追求高性能,那么到底什么是高性能,能抗很高的QPS和TPS,能有很快的响应时间等等。
  • 而我们所做的架构,是基于原始计算机的硬件设施达到最大的支撑能力,通俗的讲,就是将计算机充分利用,如CPU,内存等。别人设计的架构一台计算几能抗1WQPS,而我们设计的架构的软件再计算机上跑能抗2W QPS (相同的业务需求,相同的计算机)这就是牛逼。
2. 为什么高性能会给我们的架构带来复杂度呢?
  • 举个简单的列子,为了高性能 我们从无力架构设计的层面看,我们是不是就得引入缓存,非关系数据库,流量有高发期我们是不是就得削峰填谷,那就得引入队列的中间件,为了高性能单台不能达到我们的需求,那我们就得搞集群,搞集群就得负载均衡,搞了集群,那么数据库也得集群,数据库得主从分离/分库分表,redis也得集群。。。。。就这么一步一步的复杂下去。

3. 那我们本着什么原则才能解决随着复杂度的上升不会使我们的架构设计成指数上升呢? 来看看大姥怎么说的。

  • 简单的系统更加容易做到高性能
    1. 系统的功能越简单,影响性能的点就越少,就更加容易进行有针对性的优化。而系统很复杂的情况下,首先是比较难以找到关键性能点,因为需要考虑和验证的点太多;其次是即使花费很大力气找到了,修改起来也不容易,因为可能将 A 关键性能点提升了,但却无意中将 B 点的性能降低了,整个系统的性能不但没有提升,还有可能会下降。
    2. 有没有get到,是不是想到微服务了,把服务拆开,那当单个系统是不是就变得简单了(其他方面变复杂我们另说)
  • 可以针对单个任务进行扩展
    1. 当各个逻辑任务分解到独立的子系统后,整个系统的性能瓶颈更加容易发现,而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升,不需要改动整个系统,风险会小很多。以微信的后台架构为例,如果用户数增长太快,注册登录子系统性能出现瓶颈的时候,只需要优化登录注册子系统的性能(可以是代码优化,也可以简单粗暴地加机器),消息逻辑、LBS 逻辑等其他子系统完全不需要改动。但是不能划分的太细,适可而止。
    2. 这直接就悟了,拆解单个系统,对单个系统进行优化

2. 高可用

2.1 什么是高可用
  • 系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。
  • 单台机子可以做到无中断执行吗?貌似有点难。因为无论是单个硬件还是单个软件,都不可能做到无中断,硬件会出故障,软件会有 bug;硬件会逐渐老化,软件会越来越复杂和庞大……
  • 再说了还有自然灾害,地震给你把机房干踏了,那个想不通的同事删库跑路了等等。保证100%很难,或者说5个9等。这都还有可能
2.2 如何达到高可用?
  • 我们可以先想一想,我们使用的服务器都是在哪部署的?是不是全世界各地等等,达到高可用的根本手段是什么?那就是“冗余”
  • 详细看看大佬的总结:
  • 计算高可用
    1. 无论在哪台机器上进行计算,同样的算法和输入数据,产出的结果都是一样的(这不是函数的特性吗?)

    2.高可用是没什么问题,但是我们怎么能知道那个可用那个不可用等等。这些也就用到了我们分布式中间件,如zookpeer,springCloud等会有一些心跳检测机制,得知那台机器可用不可用

  • 存储高可用
    1. 对于需要存储数据的系统来说,整个系统的高可用设计关键点和难点就在于“存储高可用”。存储与计算相比,有一个本质上的区别:将数据从一台机器搬到到另一台机器,需要经过线路进行传输。线路传输的速度是毫秒级别,同一机房内部能够做到几毫秒;分布在不同地方的机房,传输耗时需要几十甚至上百毫秒。例如,从广州机房到北京机房,稳定情况下 ping 延时大约是 50ms,不稳定情况下可能达到 1s 甚至更多。
    2. 存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响。
    3. 综合分析,无论是正常情况下的传输延迟,还是异常情况下的传输中断,都会导致系统的数据在某个时间点或者时间段是不一致的,而数据的不一致又会导致业务问题;但如果完全不做冗余,系统的整体高可用又无法保证,所以存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响。
    4. 分布式领域里面有一个著名的 CAP 定理,从理论上论证了存储高可用的复杂度。也就是说,存储高可用不可能同时满足“一致性、可用性、分区容错性”,最多满足其中两个,这就要求我们在做架构设计时结合业务进行取舍。
2.3 高可用状态决策

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

  1. 独裁式
  • 独裁式决策指的是存在一个独立的决策主体,我们姑且称它为“决策者”,负责收集信息然后进行决策;所有冗余的个体,我们姑且称它为“上报者”,都将状态信息发送给决策者。
  • 独裁式的决策方式不会出现决策混乱的问题,因为只有一个决策者,但问题也正是在于只有一个决策者。当决策者本身故障时,整个系统就无法实现准确的状态决策。如果决策者本身又做一套状态决策,那就陷入一个递归的死循环了。
  1. 协商式
  • 协商式决策指的是两个独立的个体通过交流信息,然后根据规则进行决策,最常用的协商式决策就是主备决策。
  • 这个架构的基本协商规则可以设计成:
  • 2 台服务器启动时都是备机。
  • 2 台服务器建立连接。
  • 2 台服务器交换状态信息。
  • 某 1 台服务器做出决策,成为主机;另一台服务器继续保持备机身份。协商式决策的架构不复杂,规则也不复杂,其难点在于,如果两者的信息交换出现问题(比如主备连接中断),此时状态决策应该怎么做。
  • 如果备机在连接中断的情况下认为主机故障,那么备机需要升级为主机,但实际上此时主机并没有故障,那么系统就出现了两个主机,这与设计初衷(1 主 1 备)是不符合的。就说这么多吧,不再状态都懒得复制了眼睛疼
  1. 民主式
  • 民主式决策指的是多个独立的个体通过投票的方式来进行状态决策。例如,ZooKeeper 集群在选举 leader 时就是采用这种方式。
  • 民主式决策和协商式决策比较类似,其基础都是独立的个体之间交换信息,每个个体做出自己的决策,然后按照“多数取胜”的规则来确定最终的状态。不同点在于民主式决策比协商式决策要复杂得多,ZooKeeper 的选举算法 ZAB,绝大部分人都看得云里雾里,更不用说用代码来实现这套算法了。
  • 除了算法复杂,民主式决策还有一个固有的缺陷:脑裂。这个词来源于医学,指人体左右大脑半球的连接被切断后,左右脑因为无法交换信息,导致各自做出决策,然后身体受到两个大脑分别控制,会做出各种奇怪的动作。例如:当一个脑裂患者更衣时,他有时会一只手将裤子拉起,另一只手却将裤子往下脱。脑裂的根本原因是,原来统一的集群因为连接中断,造成了两个独立分隔的子集群,每个子集群单独进行选举,于是选出了 2 个主机,相当于人体有两个大脑了。
  • 为了解决脑裂问题,民主式决策的系统一般都采用“投票节点数必须超过系统总节点数一半”规则来处理。

就搞到这,眼睛疼。大家加油。 对我写的这篇文章感兴趣的可以看:https://time.geekbang.org/column/article/6895(阿里技术专家写的专栏)

0 人点赞