架构设计随想录

方法论

软件复杂度

软件复杂性来源于两点:本质复杂度和偶然复杂度。开发工具、开发框架、开发模式,以及高性能和高可用这些仅是偶然复杂性,架构最重要的是要解决本质复杂性,这包括人的复杂性和业务的复杂性。互联网软件通常分为功能性的复杂度和非功能性的复杂度两种。那么如何正确评估系统的复杂度呢?

架构师视角就是全局的视角,这里的全局包括空间全局和时间全局,在空间全局上你要看到整个系统的领域边界,在时间全局上你要看到整个系统的发展周期。

架构设计方案分析思路

1、谈复杂来源。在回答系统复杂度来源的时候,要注意结合具体的业务场景和业务发展阶段来阐述。业务场景表明了业务的独特性,发展阶段表明了业务的成熟度,因为同一业务场景在不同阶段产生的矛盾也是不同的。

2、谈解决方案。在回答解决方案的时候,有价值的解决方案一定是建立在明确复杂度来源基础之上的。所以在设计架构的时候才分主要问题和次要问题,主要问题是必须要解决的点,次要问题可以根据实际情况进行取舍。

3、谈评估标准。在回答如何评估架构方案时,至少要从功能性和非功能性两个角度出发判断方案的合理性。对于很难决策的方案,要从更高的视角(比如技术负责人、业务负责人的角度)进行考量。

4、说技术实现。在技术实现的细节上,要尽量讲出技术的实现原理,不要浮于表面的框架组合。

架构设计知识体系

可类比于冯诺依曼计算机的五大部件:分别是控制器、运算器、存储器、输入及输出。

1、存储器(分布式存储):数据分片、数据同步、一致性、分布式共识、CAP/BASE;

2、运算器(分布式计算):MapReduce、Lamda 等计算模式、Spark、Flink 等框架设计;

3、控制器(分布式调度):HDFS、GlusterFS、Ceph 基于存储资源的调度;Kubernetes、Mesos、Yarn 基于容器资源的调度;

4、输入与输出(分布式通信):应用层(RPC、MQ),操作系统(select、poll、epoll,以及 I/O 多路复用)、网络设备(中断和缓存)。

我自己在梳理自己的知识体系时,也是不约而同地使用了上面几个维度去夯实自己的基础知识,看来分布式领域的知识框架其实也就这样。只不过,我还加上了「分布式理论」和「框架源码」两部分的内容,前者关注分布式领域内一些理论知识,如共识算法、CAP 等,后者总结核心分布式基础框架的设计思路。

分布式理论学习路径

理论性知识的学习,都可以从三个层面进行。

  1. 加强理论深度。你可以从一个熟知的知识点出发,深入浅出地回答,比如它的工作原理、优劣势、适用场景等。

  2. 结合落地经验。你不能仅停留在理论理解,还要结合落地方案的技术实现,这样才能体现你的技术闭环思维。

  3. 构建知识体系,这是任何一个程序员向上发展的基础能力。理论深度和落地经验体现了作为程序员的基本素质,而知识体系和技术判断力则体现了你是否达到架构师的能力边界。

CAP 理论的适用范围

不同的分布式系统要根据业务场景和业务需求在 CAP 三者中进行权衡。CAP 理论用于指导在系统设计时需要衡量的因素,而非进行绝对地选择。

当网络没有出现分区时,CAP 理论并没有给出衡量 A 和 C 的因素,但如果你做过实际的分布式系统设计,一定会发现系统数据同步的时延(Latency),即例子中节点 A 同步数据到节点 A1 的时间才是衡量 A 和 C 最重要的因素,此时就不会有绝对的 AP 模型还是 CP 模型了,而是源于对实际业务场景的综合考量。

因此,才会有如 PACELC 这样的新模型优化原有的 CAP 理论,理论指导实践,实践优化理论。根据 PACELC 模型的定义,如果有网络分区产生,系统就必须在 A 和 C 之间取得平衡,否则(Else,即 PACELC 中的 E)当系统运行在无网络分区情况下,系统需要在 L(延迟)和 C 之间取得平衡。

技术选型举例:Redis 是否可以作为分布式锁?分析过程如下。

一般使用 Redis的 setnx 方法,实现锁和超时时间来控制锁的失效时间。但是在极端的情况下,当 Reids 主节点挂掉,但锁还没有同步到从节点时,根据哨兵机制,从就变成了主,继续提供服务。这时,另外的线程可以再来请求锁,此时就会出现两个线程拿到了锁的情况。

根据对 CAP 理论的理解,Redis 的设计模型是 AP 模型,而分布式锁是一个 CP 场景,那么很明显,将 Redis 这种 AP 模型的架构应用于 CP 的场景,在底层的技术选型上就是错误的。

当然,针对上述 Redis 分布式锁存在的高可用问题,Redis 的开发者 Antirez 设计了分布式锁算法 Redlock 来解决这。Redlock 算法的基本思路,是让客户端和多个独立的 Redis 实例依次请求申请加锁,如果客户端能够和半数以上的实例成功地完成加锁操作,那么我们就认为,客户端成功地获得分布式锁,否则加锁失败。这样一来,即使有某个 Redis 实例发生故障,因为锁的数据在其他实例上也有保存,所以客户端仍然可以正常地进行锁操作,锁的数据也不会丢失。那 Redlock 算法是如何做到的呢?

我们假设目前有 N 个独立的 Redis 实例, 客户端先按顺序依次向 N 个 Redis 实例执行加锁操作。这里的加锁操作和在单实例上执行的加锁操作一样,但是需要注意的是,Redlock 算法设置了加锁的超时时间,为了避免因为某个 Redis 实例发生故障而一直等待的情况。

一致性共识算法的选择

Paxos 分为 Basic Paxos 和 Multi Paxos,然而因为它们的实现复杂,工业界很少直接采用 Paxos 算法。Raft 是 Multi Paxos 的一种实现,是通过一切以领导者为准的方式,实现一系列值的共识,然而不是所有节点都能当选 Leader 领导者,Raft 算法对于 Leader 领导者的选举是有限制的,只有最全的日志节点才可以当选。

Gossip 的协议原理有一种传播机制叫谣言传播,指的是当一个节点有了新数据后,这个节点就变成了活跃状态,并周期性地向其他节点发送新数据,直到所有的节点都存储了该条数据。这种方式达成的数据一致性是 “最终一致性”,即执行数据更新操作后,经过一定的时间,集群内各个节点所存储的数据最终会达成一致,很适合动态变化的分布式系统。

共识算法的选择和数据副本数量的多少息息相关,如果副本少、参与共识的节点少,推荐采用广播方式,如 Paxos、Raft 等协议。如果副本多、参与共识的节点多,那就更适合采用 Gossip 这种最终一致性协议。

实践经验

【分布式事务】实现系统之间的分布式一致性,基于 MQ 的可靠消息队列投递方案是目前互联网最为常用的方式,在应对高并发场景下的分布式事务问题时,种方案通过放弃强一致性,而选择最终一致性,来提高系统的可用性。基于 MQ 的可靠消息投递的方案不仅可以解决由于业务流程的同步执行而造成的阻塞问题,还可以实现业务解耦合流量削峰。

【分布式存储】数据分片即按照一定的规则将数据路由到相应的存储节点中,从而降低单存储节点带来的读写压力。常见的数据分片实现方案有 Hash(哈希分片)与 Range(范围分片)。Hash 分片是一种静态的分片方式,必须要提前设定分片的最大规模,而且无法避免单一热点问题,Range 分片能结合业务逻辑规则,可以一定程度上解决单一热点问题。

作者

Yves

发布于

2021-09-07

更新于

2021-09-07

许可协议