关于分布式系统相关的内容, 在之前的博客里面也提到过不少,主要包括一致性算法、分布式存储等相关的内容,但是对于分布式系统,并没有一个清晰的概念。今天主要来看一下分布式系统的基础概念和理论基础,分布式系统涉及很多的技术、理论和协议,很多人也说,分布式系统是“入门容易,深入难”,我之前的学习也只算是管中窥豹,只见得其中一斑。

在网上搜索“如何学习分布式系统”,看完之后还是觉得云里雾里,不进行记录不太容易理清楚这里边的内容。本系列的博客主要关注一些实际应用场景中的技术实现,偏向于算法方向,更多的还是作为了解的内容。

什么是分布式系统?

一个分布式系统是一些独立的计算机集合,但是对这个系统的用户来说,系统就像一台计算机一样。

首先需要明确的是,只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升(加内存、加磁盘、使用更好的CPU)高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的,而由于分布式系统多节点、通过网络通信的拓扑结构,会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。。。

分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。简单来说就是一群独立计算机集合共同对外提供服务,但是对于系统的用户来说,就像是一台计算机在提供服务一样。分布式意味着可以采用更多的普通计算机(相对于昂贵的大型机)组成分布式集群对外提供服务。计算机越多,CPU、内存、存储资源等也就越多,能够处理的并发访问量也就越大。

从分布式系统的概念中我们知道,各个主机之间通信和协调主要通过网络进行,所以分布式系统中的计算机在空间上几乎没有任何限制,这些计算机可能被放在不同的机柜上,也可能被部署在不同的机房中,还可能在不同的城市中,对于大型的网站甚至可能分布在不同的国家和地区。

在很多文章中,主要讲分布式系统分为分布式计算(computation)与分布式存储(storage)。计算与存储是相辅相成的,计算需要数据,要么来自实时数据(流数据),要么来自存储的数据;而计算的结果也是需要存储的。在操作系统中,对计算与存储有非常详尽的讨论,分布式系统只不过将这些理论推广到多个节点罢了。

那么分布式系统怎么将任务分发到这些计算机节点呢,很简单的思想,分而治之,即分片(partition)。对于计算,那么就是对计算任务进行切换,每个节点算一些,最终汇总就行了,这就是MapReduce的思想;对于存储,更好理解一下,每个节点存一部分数据就行了。当数据规模变大的时候,Partition是唯一的选择,同时也会带来一些好处:

  1. 提升性能和并发,操作被分发到不同的分片,相互独立

  2. 提升系统的可用性,即使部分分片不能用,其他分片不会受到影响

理想的情况下,有分片就行了,但事实的情况却不大理想。原因在于,分布式系统中有大量的节点,且通过网络通信。单个节点的故障(进程crash、断电、磁盘损坏)是个小概率事件,但整个系统的故障率会随节点的增加而指数级增加,网络通信也可能出现断网、高延迟的情况。在这种一定会出现的“异常”情况下,分布式系统还是需要继续稳定的对外提供服务,即需要较强的容错性。最简单的办法,就是冗余或者复制集(Replication),即多个节点负责同一个任务,最为常见的就是分布式存储中,多个节点复杂存储同一份数据,以此增强可用性与可靠性。同时,Replication也会带来性能的提升,比如数据的locality可以减少用户的等待时间。

img

Partition和Replication是解决分布式系统问题的一记组合拳,很多具体的问题都可以用这个思路去解决。但这并不是银弹,往往是为了解决一个问题,会引入更多的问题,比如为了可用性与可靠性保证,引用了冗余(复制集)。有了冗余,各个副本间的一致性问题就变得很头疼,一致性在系统的角度和用户的角度又有不同的等级划分。如果要保证强一致性,那么会影响可用性与性能,在一些应用(比如电商、搜索)是难以接受的。如果是最终一致性,那么就需要处理数据冲突的情况。CAP、FLP这些理论告诉我们,在分布式系统中,没有最佳的选择,都是需要权衡,做出最合适的选择。

分布式系统的主要特征

无论空间上如何分布,一个标准的分布式系统应该具有以下几个主要特征

  • 分布性

分布式系统中的多台计算机之间在空间位置上可以随意分布,同时,机器的分布情况也会随时变动。

  • 对等性

分布式系统中的计算机没有主/从之分,即没有控制整个系统的主机,也没有被控制的从机,组成分布式系统的所有计算机节点都是对等的。副本(Replica)是分布式系统最常见的概念之一,指的是分布式系统对数据和服务提供的一种冗余方式。在常见的分布式系统中,为了对外提供高可用的服务,我们往往会对数据和服务进行副本处理。数据副本是指在不同节点上持久化同一份数据,当某一个节点上存储的数据丢失时,可以从副本上读取该数据,这是解决分布式系统数据丢失问题最为有效的手段。另一类副本是服务副本,指多个节点提供同样的服务,每个节点都有能力接收来自外部的请求并进行相应的处理。

  • 自治性

分布式系统中的各个节点都包含自己的处理机和内存,各自具有独立的处理数据的功能。通常,彼此在地位上是平等的,无主次之分,既能自治地进行工作,又能利用共享的通信线路来传送信息,协调任务处理。

  • 并发性

在一个计算机网络中,程序运行过程的并发性操作是非常常见的行为。例如同一个分布式系统中的多个节点,可能会并发地操作一些共享的资源,如何准确并高效地协调分布式并发操作也成为了分布式系统架构与设计中最大的挑战之一。

分布式系统面临的问题

  • 缺乏全局时钟

在分布式系统中,很难定义两个事件究竟谁先谁后,原因就是因为分布式系统缺乏一个全局的时钟序列控制。

  • 机器宕机

机器宕机是最常见的异常之一。在大型集群中每日宕机发生的概率为千分之一左右,在实践中,一台宕机的机器恢复的时间通常认为是24 小时,一般需要人工介入重启机器。

  • 网络异常

消息丢失,两片节点之间彼此完全无法通信,即出现了“网络分化”;消息乱序,有一定的概率不是按照发送时的顺序依次到达目的节点,考虑使用序列号等机制处理网络消息的乱序问题,使得无效的、过期的网络消息不影响系统的正确性;数据错误;不可靠的TCP,TCP 协议为应用层提供了可靠的、面向连接的传输服务,但在分布式系统的协议设计中不能认为所有网络通信都基于TCP 协议则通信就是可靠的。TCP协议只能保证同一个TCP 链接内的网络消息不乱序,TCP 链接之间的网络消息顺序则无法保证。

  • 分布式三态

如果某个节点向另一个节点发起RPC(Remote procedure call)调用,即某个节点A 向另一个节点B 发送一个消息,节点B 根据收到的消息内容完成某些操作,并将操作的结果通过另一个消息返回给节点A,那么这个RPC 执行的结果有三种状态:“成功”、“失败”、“超时(未知)”,称之为分布式系统的三态。

  • 存储数据丢失

对于有状态节点来说,数据丢失意味着状态丢失,通常只能从其他节点读取、恢复存储的状态。 异常处理原则:被大量工程实践所检验过的异常处理黄金原则是:任何在设计阶段考虑到的异常情况一定会在系统实际运行中发生,但在系统实际运行遇到的异常却很有可能在设计时未能考虑,所以,除非需求指标允许,在系统设计时不能放过任何异常情况。

衡量分布式系统的指标

  • 性能

系统的吞吐能力,指系统在某一时间可以处理的数据总量,通常可以用系统每秒处理的总的数据量来衡量;系统的响应延迟,指系统完成某一功能需要使用的时间;系统的并发能力,指系统可以同时完成某一功能的能力,通常也用QPS(query per second)来衡量。上述三个性能指标往往会相互制约,追求高吞吐的系统,往往很难做到低延迟;系统平均响应时间较长时,也很难提高QPS。

  • 可用性

系统的可用性(availability)指系统在面对各种异常时可以正确提供服务的能力。系统的可用性可以用系统停服务的时间与正常服务的时间的比例来衡量,也可以用某功能的失败次数与成功次数的比例来衡量。可用性是分布式的重要指标,衡量了系统的鲁棒性,是系统容错能力的体现。

  • 可扩展性

系统的可扩展性(scalability)指分布式系统通过扩展集群机器规模提高系统性能(吞吐、延迟、并发)、存储容量、计算能力的特性。好的分布式系统总在追求“线性扩展性”,也就是使得系统的某一指标可以随着集群中的机器数量线性增长。

  • 一致性

分布式系统为了提高可用性,总是不可避免的使用副本的机制,从而引发副本一致性的问题。越是强的一致的性模型,对于用户使用来说使用起来越简单。

分布式基础理论

同步/异步系统模型

同步系统模型:指系统中的各个节点的时钟误差存在上限,并且消息传递必须在一定时间内完成,否则认为失败;同时各个节点完成处理消息的时间是一定的。因此同步系统中可以很容易地判断消息是否丢失。

异步系统模型:系统中各个节点可能存在较大的时钟差异;同时消息传输时间是任意长的;各节点对消息进行处理的时间也可能是任意长的。这就造成无法判断某个消息迟迟没有被响应是哪里出了问题(节点故障还是传输故障)。现实生活中的系统往往都是异步系统。

FLP 不可能原理

由 Fischer,Lynch 和 Patterson 三位科学家发表的《Impossibility of Distributed Consensus with One Faulty Process》论文中提出,在网络可靠,但允许节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性共识算法

描述:FLP不可能原理假定节点只能因崩溃而失败; 网络可靠,并且异步系统模型的典型时序假设成立:例如,消息延迟没有限制的情况下,假设有A、B、C三个节点进行投票,A投票0,B投票1,而C收到了A与B的投票却没办法响应,A与B就没办法在有限的时间内获知最终结果;如果进行重新投票,类似的情况重复发生,则永远无法达到共识。

FLP 不可能原理的意义在于,告诉我们不要浪费时间去为异步分布式系统设计在任意场景上都能够实现共识的算法,异步系统完全没有办法保证能在有限时间内达成一致。

CAP 理论

CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点,只能满足三项中的两项:

  • 一致性(Consistency) : 任何事务都应该是原子的,所有副本上的状态都是事务成功提交后的结果,并保持强一致性。
  • 可用性(Availability) : 系统正常节点能在有限时间内完成对操作请求的应答。
  • 分区容错性(Partition tolerance) : 系统中的网络可能发生分区故障(成为多个子网、节点上线和下线),节点之间的通信无法保障,而网络故障不应该影响到系统正常服务。

图片

CAP理论证明

假设有两个通信中的节点出现了网络分区的情况,如果允许其中一个节点更新状态,则需要舍弃一致性(C);如果为了保证数据一致性,将分区的节点设置为不可用,就需要舍弃可用性(A);如果两个节点可以互相通信,才能既保证一致性又保证可用性,会丧失分区容错性(P)。

三类系统模型

  • CA(一致性+可用性):包括完全严格的仲裁协议,例如2PC(两阶段提交)
  • CP(一致性+分区容错性): 包括多数仲裁协议,其中少数分区不可用,例如Paxos
  • AP(可用性+分区容错性): 包括执行最终一致性的协议,例如Gossip

CA\CP区别:CA和CP系统设计均提供相同的一致性模型:高度一致性。 唯一的区别是CA系统不能容忍任何节点故障。 CP系统可以容忍 f 在给定 2f+1 在非拜占庭式故障模型中。

场景

  • CA:弱化了分区容错性,早期分布式关系数据库系统中使用的许多系统设计如两阶段提交,都没有考虑分区容错性。 分区容错性是现代系统的重要属性,因为如果系统在多个地理环境上分布,网络分区出现的概览就会加大。
  • CP:弱化了可用性,一些对结果一致性很敏感的应用会选择基于此模型设计,当系统出现故障时会拒绝服务;Paxos、Raft 等共识算法,以及HBase、MongoDB等基于此模型设计。
  • AP:弱化了一致性,一些对结果一致性不敏感的应用会选择基于此模型设计,可以允许在新版本上线后过一段时间才最终更新成功,期间不保证一致性;分布式同步协议如 Gossip,以及DynamoDB、 CouchDB、Cassandra 数据库等基于此模型设计。

ACID原则与BASE原则

ACID原则

ACID 即 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)四种特性的缩写,一般出现在分布式数据库等基于事务过程的系统中;ACID 原则描述了分布式数据库需要满足的一致性需求,同时允许付出可用性的代价。

  • Atomicity: 每次事务是原子的,事务包含的所有操作要么全部成功,要么全部不执行。一旦有操作失败,则需要回退状态到执行事务之前;
  • Consistency: 数据库的状态在事务执行前后的状态是一致的和完整的,无中间状态。即只能处于成功事务提交后的状态;
  • Isolation: 各种事务可以并发执行,但彼此之间互相不影响。按照标准 SQL 规范,从弱到强可以分为未授权读取、授权读取、可重复读取和串行化四种隔离等级;
  • Durability: 状态的改变是持久的,不会失效。一旦某个事务提交,则它造成的状态变更就是永久性的。

BASE原则

BASE即 Basic Availability(基本可用),Soft-state(弱状态),Eventual Consistency(最终一致性),为 eBay 技术专家 Dan Pritchett 提出的与ACID相对的一个原则,主要面向大型高可用分布式系统,主张牺牲掉对强一致性的追求,而实现最终一致性,来换取一定的可用性。

  • Basic Availability:系统在出现不可预知的故障时候,允许损失部分可用性,保证核心服务可用。
  • Soft-state:允许系统在不同节点的数据副本之间进行数据同步的过程中存在延时(允许系统中的数据存在中间状态,不会影响系统的整体可用性)。
  • Eventual Consistency:系统中所有的数据副本,在进过一段时间的同步后,最终能够达到一个一致的状态。

分布式系统下的一致性问题

一致性为在分布式系统领域中对于多个服务节点,给定一系列操作,在约定协议的保障下,使得它们对处理结果达成某种程度的协同。

分布式系统中的节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。基于消息传递通信模型的分布式系统,不可避免的会发生以下错误:进程可能会响应慢、被杀死或者重启,消息可能会延迟、丢失、重复;发生上面任意一种异常都会对分布式系统中各个节点对某一个值达成一致性产生问题。

一致性的要求

  • 可终止性(Termination):一致的结果在有限时间内能完成(可以保障提供服务的(Liveness))
  • 约同性(Agreement):不同节点最终完成决策的结果是相同的(意味着算法要么不给出结果,任何给出的结果必定是达成了共识的,即安全性(Safety))
  • 合法性(Validity):决策的结果必须是某个节点提出的提案(即达成的结果必须是节点执行操作的结果)

解决一致性问题的核心在于对不同空间发生的事件进行全局唯一排序。

一致性模型

  • 强一致性模型
    • 顺序一致性:所有操作都以某种顺序原子执行,该顺序与各个节点上看到的顺序一致,并且在所有节点上都相等;可以基于Lamport timestamp 即逻辑时钟进行实现。
    • 线性一致性:所有操作都按照操作的全局实时顺序一致的顺序自动执行;在顺序一致性前提下加强了进程间的操作排序,形成唯一的全局顺序;依赖于全局的时钟或锁,有很强的原子性保证,但是比较难实现。
  • 弱一致性模型
    • 最终一致性:在未来的某个时间点进行冲突检测和修正,如DNS
    • 客户端为中心型一致性:通过在client端库中建立额外的缓存来实现,如亚马逊Dynamo

共识算法

共识(Consensus)与一致性(Consistency)

一致性:含义比共识宽泛,在不同场景(基于事务的数据库、分布式系统等)下意义不同。在分布式系统场景下,一致性指的是多个副本对外呈现的状态。如之前提到的顺序一致性、线性一致性,描述了多节点对数据状态的共同维护能力。

共识:特指在分布式系统中多个节点之间对某个事情达成一致看法的过程。需注意达成某种共识并不意味着就保障了一致性。

共识算法解决的问题

共识算法解决的是分布式系统对某个提案(Proposal),大部分节点达成一致意见的过程。提案泛指多个事件发生的顺序、某个键对应的值…对于分布式系而言,各个节点通常都是相同的确定性状态机模型(又称为状态机复制问题,State-Machine Replication),从相同初始状态开始接收相同顺序的指令,则可以保证相同的结果状态。

这里共识算法需要解决两个基本问题:

  1. 如何提出一个待共识的提案(令牌传递、随机选取…)
  2. 如何让多个节点对提案达成共识(投票、规则验证…)

现实网络环境中存在各种各样的问题,在分布式环境下,共识算法还需要解决如通信问题(网络中断、分区)、节点故障、消息伪造…

共识算法分类

根据是否允许拜占庭错误(伪造信息恶意响应)的情况,共识算法分为 Crash Fault Tolerance 崩溃容错 (CFT) 和 Byzantine Fault Tolerance(BFT)两类。

Crash Fault Tolerance (CFT) 算法:Paxos、Raft、ZAB…

Byzantine Fault Tolerance(BFT) 算法:PBFT为代表的确定性系列算法、PoW为代表的概率算法…

核心问题-复制

为什么核心问题是复制:在文章开头我们说过,分布式系统采用分片来将任务分发到这些计算机节点,为了实现高可用,又引入了冗余。分布式存储相关的系统都必须用某种冗余的方式在廉价硬件的基础上搭建高可靠的存储,而冗余的基础就是复制(多副本策略), 一份数据存多份. 多副本保证了可靠性, 而副本之间的一致, 就需要各种分布式共识算法来保证。

复制是一个组通信问题。需要考虑哪种通信方式可以为我们提供我们想要的性能和可用性特性?面对网络分区以及节点同时发生故障,我们如何确保容错性,持久性以及避免分歧。

基本复制方式

  • 同步复制:强持久化保证,系统响应慢,对网络延迟敏感
  • 异步复制:弱持久化保证,性能高,对网络延迟更加宽容

基本复制算法

基本复制算法大致可以分为两类:Replication methods that prevent divergence (single copy systems) 防止差异的复制方式(单拷贝系统)与Replication methods that risk divergence (multi-master systems) 有差异风险的复制方式(多主系统)

Replication Methods that Prevent Divergence (single Copy systems)

防止差异的复制方式(单拷贝系统)

对外表现得像一个单独的系统;当部分故障发生时,系统确保只有一个系统副本处于活动状态;系统需要确保副本始终保持一致,基于某一种共识算法去实现,一般有如下几种方式:

Master/Slave(主从复制)

所有更新都在主服务器上执行,操作日志(或者更改)通过网络传送到备份副本;涉及两种相关的变体异步主/备份、同步主/备份、半同步主/备复制。

  1. 同步复制: 直到数据真的安全的复制到全部的机器上之后, master才告知客户端数据已经完成同步

image

问题:强一致性持久化保证,但是系统响应慢,对网络延迟的变化非常敏感;并且系统的可用性随着副本数量指数降低,任何一个机器的宕机都会影响到整个系统的写入。

  1. 异步复制: master将更新存储在本地后立即向客户端发回响应,master在之后才进行异步复制到全部的机器上。

image

问题:性能高,但是为弱一致性持久化保证,数据存在丢失风险,会造成数据不一致的情况。

  1. 半同步复制:要求master在应答客户端之前必须把数据复制到足够多的机器上, 而非全部机器. 这样副本数够多可以提供比较高的可靠性; 1台机器宕机也不会让整个系统停止写入; 但系统中还是会存在数据不一致的情况。

2-phase commit(两阶段提交)

阶段一:投票阶段,协调人向所有参与者发送更新信息。每个参与者处理更新,并投票决定是提交还是放弃。当投票决定提交时,参与者将更新存储到一个临时区域(write-ahead log)。

阶段二:协调程序决定结果并通知每个参与者。如果所有参与者投票提交,那么更新将从临时区域获得并永久化。

问题:强一致性持久化保证,但是系统响应慢,对网络延迟的变化非常敏感;系统的可用性随着副本数量指数降低

1
2
3
4
[ Coordinator ] -> OK to commit?     [ Peers ]
<- Yes / No
[ Coordinator ] -> Commit / Rollback [ Peers ]
<- ACK

Quorum机制(多数派)

Quorum 机制,是一种分布式系统中常用的,用来保证数据冗余和最终一致性的投票算法,其主要数学思想来源于鸽巢原理;在分布式系统中,Quorum常用于副本的读写控制,容忍最多 (N-1)/2 个节点损坏。

假设每份数据有V个副本,每个副本对应一票,读、写操作首先要请求副本以获取其票数,定义:

1
2
read quorum R(最小读票数):读操作获取的票数必须大于该值才允许读;
write quorum W(最小写票数):写操作获取的票数必须大于该值才允许写;

V、R、W必须满足:

  • R + W > V:保证对于每份数据,不会 同时读和写(当一个写操作请求过来的时候,它必须要获得W个写票。而剩下的数量是V-W是不够R的,因此不能再有读请求过来了)。
  • W > V / 2:保证对于每份数据,不会同时出现 两个写,即写操作是串行的

其他

  • 没有规定 R > V / 2,quorum 机制允许 多个读同时发生,即允许 并发读;
  • 考虑write -> read序列,因为R + W > V,因此 W 和 V 之间至少有一个重叠(鸽巢原理),从而保证 write 之后,read 操作至少会获取一个最新副本;
  • 在做复制冗余的时候,借助 Quorum 机制,5 个副本只需要完成 3 个写即可响应成功,提升了写操作的响应速度,又没有减弱可靠性;Quorum 机制本质上是把写负载转移到了读负载的一种设计权衡。

问题

  • 读取不一致状态情况:对于一条数据的更新时, 会产生不一致的状态问题:如第一次client update,nodeA、nodeB写入a=x;第二次client update,nodeB、nodeC写入a=y;如果读取a的客户端联系到nodeA和nodeB,会得到不一致的数据(解决:对每次的写入增加全局时间戳,以后写入的优先)
1
2
3
nodeA: a=x 1577851200000
nodeB: a=y 1577851230000
nodeC: a=y 1577851230000
  • 多数派写异常情况:在完成一起完整的多数派写时,发生写入异常,会产生不一致的状态问题:如第一次client update,nodeA、nodeB写入a=x;第二次client update,nodeB、nodeC写入a=y;但是只有nodeC写入成功了,然后client abort了,这时候另一个client 读取到nodeA与nodeB得到的结果与读取到nodeB与nodeC的不一致。
1
2
3
nodeA: a=x 1577851200000
nodeB: a=x 1577851200000
nodeC: a=y 1577851230000
  • 并发环境下,因为无法保证顺序执行,所以无法保证系统的正确性。

结论

Quorum机制无法保证强一致性,即无法实现任何时刻任何用户或节点都可以读到最近一次成功提交的副本数据;后续Paxos对Quorum机制进行了改进,通过2次多数派读写, 实现了严谨的强一致共识算法。

Replication Methods that risk Divergence (multi-master systems)

有差异风险的复制方式(多主系统)

Gossip算法

Gossip算法Palo Alto研究中心在论文《Epidemic Algorithms for Replicated Database Maintenance》中提出的一种用于分布式数据库在多节点间复制同步数据的算法;特点是要同步的信息如同流言一般传播,最终一致性。

具体的工作过程如下:

  1. 如果有某一项信息需要在整个网络中所有节点中传播,那从信息源开始,选择一个固定的传播周期(如1秒),随机选择它 相连接的k个节点(称为Fan-Out)进行消息传播。
  2. 每一个节点收到消息后,如果这个消息是它之前没有收到过的,将在下一个周期内,选择除了发送消息给它的那个节点外的 其他相邻k个节点发送相同的消息,理论上最终网络的所有节点都会拥有相同的消息。

image

上图从一致性、延迟、吞吐量、数据丢失和故障转移对比了各个类型共识算法实现。

用一个请求串起来

假设这是一个对外提供服务的大型分布式系统,用户连接到系统,做一些操作,产生一些需要存储的数据,那么在这个过程中,会遇到哪些组件、理论与协议呢?

用户使用Web、APP、SDK,通过HTTP、TCP连接到系统。在分布式系统中,为了高并发、高可用,一般都是多个节点提供相同的服务。那么,第一个问题就是具体选择哪个节点来提供服务,这个就是负载均衡(load balance)。负载均衡的思想很简单,但使用非常广泛,在分布式系统、大型网站的方方面面都有使用,或者说,只要涉及到多个节点提供同质的服务,就需要负载均衡。

通过负载均衡找到一个节点,接下来就是真正处理用户的请求,请求有可能简单,也有可能很复杂。简单的请求,比如读取数据,那么很可能是有缓存的,即分布式缓存,如果缓存没有命中,那么需要去数据库拉取数据。对于复杂的请求,可能会调用到系统中其他的服务。

承上,假设服务A需要调用服务B的服务,首先两个节点需要通信,网络通信都是建立在TCP/IP协议的基础上,但是,每个应用都手写socket是一件冗杂、低效的事情,因此需要应用层的封装,因此有了HTTP、FTP等各种应用层协议。当系统愈加复杂,提供大量的http接口也是一件困难的事情。因此,有了更进一步的抽象,那就是RPC(remote produce call),是的远程调用就跟本地过程调用一样方便,屏蔽了网络通信等诸多细节,增加新的接口也更加方便。

一个请求可能包含诸多操作,即在服务A上做一些操作,然后在服务B上做另一些操作。比如简化版的网络购物,在订单服务上发货,在账户服务上扣款。这两个操作需要保证原子性,要么都成功,要么都不操作。这就涉及到分布式事务的问题,分布式事务是从应用层面保证一致性:某种守恒关系。

上面说道一个请求包含多个操作,其实就是涉及到多个服务,分布式系统中有大量的服务,每个服务又是多个节点组成。那么一个服务怎么找到另一个服务(的某个节点呢)?通信是需要地址的,怎么获取这个地址,最简单的办法就是配置文件写死,或者写入到数据库,但这些方法在节点数据巨大、节点动态增删的时候都不大方便,这个时候就需要服务注册与发现:提供服务的节点向一个协调中心注册自己的地址,使用服务的节点去协调中心拉取地址。

从上可以看见,协调中心提供了中心化的服务:以一组节点提供类似单点的服务,使用非常广泛,比如命令服务、分布式锁。协调中心最出名的就是chubby,zookeeper。

回到用户请求这个点,请求操作会产生一些数据、日志,通常为信息,其他一些系统可能会对这些消息感兴趣,比如个性化推荐、监控等,这里就抽象出了两个概念,消息的生产者与消费者。那么生产者怎么讲消息发送给消费者呢,RPC并不是一个很好的选择,因为RPC肯定得指定消息发给谁,但实际的情况是生产者并不清楚、也不关心谁会消费这个消息,这个时候消息队列就出马了。简单来说,生产者只用往消息队列里面发就行了,队列会将消息按主题(topic)分发给关注这个主题的消费者。消息队列起到了异步处理、应用解耦的作用。

上面提到,用户操作会产生一些数据,这些数据忠实记录了用户的操作习惯、喜好,是各行各业最宝贵的财富。比如各种推荐、广告投放、自动识别。这就催生了分布式计算平台,比如Hadoop,Storm等,用来处理这些海量的数据。

最后,用户的操作完成之后,用户的数据需要持久化,但数据量很大,大到按个节点无法存储,那么这个时候就需要分布式存储:将数据进行划分放在不同的节点上,同时,为了防止数据的丢失,每一份数据会保存多分。传统的关系型数据库是单点存储,为了在应用层透明的情况下分库分表,会引用额外的代理层。而对于NoSql,一般天然支持分布式。

一个简化的架构图

  下面用一个不大精确的架构图,尽量还原分布式系统的组成部分(不过只能体现出技术,不好体现出理论)

img

概念与实现

那么对于上面的各种技术与理论,业界有哪些实现呢,下面进行简单罗列。

当然,下面的这些实现,小部分我用过,知其所以然;大部分听说过,知其然;还有一部分之前闻所未闻,分类也不一定正确,只是从其他文章抄过来的。罗列在这里,以便日后或深或浅的学习。

  • 负载均衡:

    • Nginx:高性能、高并发的web服务器;功能包括负载均衡、反向代理、静态内容缓存、访问控制;工作在应用层

    • LVS: Linux virtual server,基于集群技术和Linux操作系统实现一个高性能、高可用的服务器;工作在网络层

  • webserver:

    • Java:Tomcat,Apache,Jboss
    • Python:gunicorn、uwsgi、twisted、webpy、tornado
  • service:  

    • SOA、微服务、spring boot,django
  • 容器:

    • docker,kubernetes
  • cache:

    • memcache、redis等
  • 协调中心:

    • zookeeper、etcd等
    • zookeeper使用了Paxos协议Paxos是强一致性,高可用的去中心化分布式。zookeeper的使用场景非常广泛,之后细讲。
  • rpc框架:

    • grpc、dubbo、brpc
    • dubbo是阿里开源的Java语言开发的高性能RPC框架,在阿里系的诸多架构中,都使用了dubbo + spring boot
  • 消息队列:

    • kafka、rabbitMQ、rocketMQ、QSP
    • 消息队列的应用场景:异步处理、应用解耦、流量削锋和消息通讯
  • 实时数据平台:

    • storm、akka
  • 离线数据平台:

    • hadoop、spark
    • PS: apark、akka、kafka都是scala语言写的,看到这个语言还是很牛逼的
  • dbproxy:

    • cobar也是阿里开源的,在阿里系中使用也非常广泛,是关系型数据库的sharding + replica 代理
  • db:

    • mysql、oracle、MongoDB、HBase
  • 搜索:

    • elasticsearch、solr
  • 日志:

    • rsyslog、elk、flume

总结

学习分布式系统还是比较困难的,资料少而且实战的机会也不多,多以更多还是停留在纸面上的一些东西,所以我们在开头也提到了,本系列的博客更多还是关注分布式系统在实际生产环境中的一些使用方式。

有了坏消息,就必然会有好消息。坏消息是腾讯的两个全都挂了,好消息是今天字节跳动约了二面,但是我心里没底,害怕又像腾讯二面一样,所以明天和后天上午还是好好看一下八股和项目。一直说已经把项目吃透了,其实并没有,下次面试的时候一定要详细地讲自己的项目,技术选型和数据结构都要说,不能再省略过去了。自信点、自信点、自信点,不要说着说着就没声音了,哪怕不会也不能没气势。进字节不就是一开始学 Go 的原因吗,好好珍惜这次机会。加油,祝我面试成功!!!