原创声明:本文系作者原创,谢绝个人、媒体、公众号或网站未经授权转载,违者追究其法律责任。
在 20 世纪 60 年代,大型主机凭借其超强的计算和 IO 处理能力以及在稳定性和安全性方面的卓越表现,在很长的一段时间里引领了计算机行业的发展。随着大型主机的发展,集中式的计算机系统架构也成为了主流。随着计算需求的增长和计算场景的多样化,集中式的处理模式越来越显得捉襟见肘,同时随着 PC 技术的成熟和普及,PC 机也进入各行各业,成为更容易获取的计算资源,但随之也产生了大量闲散的计算单元,网格计算的概念被随之提出,并随着计算机网络化和微型化的发展趋势,不断演进发展。整个分布式计算的理论和实践也走向成熟,计算机系统也开始从集中式向分布式架构的演进。
在前一篇《集中式架构与分布式架构比较》中,我们已经对两种风格的架构进行了详细对比,显然分布式架构具有明显的优势,在此不再赘述。本专题中将聚焦分析分布式架构的几个关键方面,在本文我们将探讨分布式架构的主要组成要素及其运行机制。
分布式系统的主要特征
分布式系统是其组件分布在连网的计算机上,组件之间通过传递消息进行通信和动作协调的系统。一个标准的分布式系统在没有任何特定业务逻辑约束的情况下,一般有以下几个特征:
分布性:一个分布式系统中的计算机在空间部署上是可以随意分布的,这些计算机可能处于不同的机柜上,不同的城市的机房中,或者是世界上任何一个角落。同时,机器的分布情况也会随时变动;
并发性:在一个计算机网络中,程序运行过程中的并发性操作是非常常见的行为,例如同一个分布式系统的多个节点,可能会并发地操作一些共享的资源,诸如数据库或分布式存储等,如何准确并高效地协调分布式并发操作也成为了分布式系统架构与设计中最大的挑战之一;
缺乏全局时钟:分布式系统中多个进程可能在空间上分布式在各处,进程之间通过交换消息来相互协作,密切的协作通常取决于对程序动作发生的时间的共识。但在分布式系统中,各进程与时钟同步所达到的准确性是有限的,即没有一个正确时间的全局概念;
故障独立性:组成分布式系统的所有计算机,都有可能发生任何形式的故障。单个模块的故障相对容易预知并设计应对逻辑,但分布式系统可能以新的方式出现故障。网络故障导致互连的计算机的隔离,但它们并不一定会停止运行,而且程序很难判断是网络故障还是因为延迟。同样,当被网络隔离计算程序在异常终止时,也许不能马上通知与它通信的其他组件了解。系统的每个组件会单独地出现故障,而其他组件还在运行。
分布式系统能够实现高可用、高吞吐、大容量存储、海量计算、并行计算等优异能力,天然的分布性和可伸缩等特性,也打破了物理上单机的瓶颈,使其能不断支撑着业务的发展而演进,并推进了云计算、大数据、人工智能等领域的发展。但是正如每个硬币都有两面,分布式系统的复杂性,也使其在设计、研发、运行、维护、安全性等方面都面临更多的挑战。
主要组成要素及其运行机制
分布式服务
分布式系统下节点间服务通讯有两种途径,一种是通过 RPC(Remote Procedure Call) 实现两点间通讯,可以设计成同步通讯,也可以是异步的;一种是通过消息中间件实现通信,一般是异步通讯方式。
RPC 调用
RPC 调用也是分布式环境下常见的通讯方式,有同步的模式,也有异步的模式。同步模式下,客户端发起调用并阻塞当前线程,直到服务端处理完毕返回响应才完成整个调用过程;异步模式下,客户端发起调用后立即返回,并记录当前请求,直到接收到服务端响应后才找到对应的请求进行结果的处理,并完成整个调用过程。
RPC 调用中一般只有两个角色:客户端和服务端,但在分布式环境下服务端可能有多台机器,他们共同组成一个集群,每台都提供一样的服务。这时就要有一种机制能让客户端将持续不断的请求均衡的分发到所有的服务端,一般将这种机制称为「负载均衡」。负载均衡可以在客户端实现,也可以通过专门的负载均衡设备实现。如果采用客户端负载均衡方案,需要让客户端感知所有服务端的机器地址和端口,需要引入一套「服务发现和集成的体系」,好处是通讯是点对点的,比较高效,且不存在单点。如果使用专门负载均衡设备,则客户端只需感知负载均衡设备的地址,由它来对接所有服务端,并均衡服务请求,这种模式比较简单,但存在性能单点,且延长了请求链路,稳定性也会受影响。无论是客户端还是专门设备做负载聚合,都可以根据不同的场景采用适合的负载均衡策略,常见的策略有随机(Random)、轮询(RoundRobin)、最快连接数(Fastest Connection)、最小连接数(Least Connection)等等。
消息中间件
消息中间件一般是独立部署的一组应用程序,负责消息的接收和投递。整个通讯过程是异步的,有消息发布者、消息中间件和消息订阅者三个角色,消息从发布端发出后,会被消息中间件接收并做持久化,消息的消费有两种常见的模式,一种是消息中心主动投递的模式(推模式),一种是订阅者主动拉取的模式(拉模式)。在推模式下,发布者发布消息,消息发到消息中心做持久化,然后被投递到订阅端集群,一般来说会选择在所有订阅者中随机选一台投递,在这种模式下,要求消息订阅者集群的消息消费速度要能跟上发布消息是速度;在拉模式下,消费者根据队列里消息总数来做均分,主动连上消息中间件进行消费,对消费速度没有速度限制,所以一般拉模式的消息中间件会提供更强的消息扛积压能力。
由于消息被持久化,所以可以做到消息的可靠投递,保证了分布式环境下通讯的可靠达到。在分布式环境下,一个系统可能会在一次事务操作中通过消息的方式去改变其它系统的状态(主要指存储的状态),为了能模拟类似事务的一致性,异步消息可以利用 DB 的持久化来支持事务型消息。事务型消息指的是发布消息的应用系统在本地数据库事务操作序列中发送的消息。此类消息的投递与数据库事务状态保持一致,当事务状态是提交时,消息会被投递到订阅者,当事务状态是回滚时,消息不会被投递到订阅者,这也是分布式环境下消息中间件需要支持的特殊场景。
分布式数据存储
分布式缓存
当传统数据库面临大规模数据访问时,磁盘 I/O 能力往往成为性能瓶颈,从而导致过高的响应延迟,而比较常用的提升性能的手段,是在应用和数据库之间加入一层分布式缓存来提升数据访问性能,分布式缓存将高速内存作为数据对象的存储介质,理想情况下可以获得内存级的读写性能,常见的缓存实现会通过 LRU 算法来缓存访问最多的数据,进而提升缓存效率。
通过分布式缓存服务器集群,将缓存数据分布到集群多台服务器上可在一定程度上改善缓存的可用性,同时也能起到扩展缓存容量的作用,当一台缓存服务器宕机的时候,只有部分缓存数据丢失,重新从数据库加载这部分数据不会对数据库产生很大影响。
分布式缓存还具有支持弹性扩展的能力,通过动态增加或减少节点,应对变化的数据访问负载,提供可预测的性能与扩展性,同时也能最大限度地提高资源利用率
分布式缓存比较典型应用场景包括:
应用对象缓存:缓存系统作为 ORM 框架的二级缓存对外提供服务,目的是减轻数据库的负载压力,加速应用访问;
状态缓存:缓存包括 Session 会话状态及应用横向扩展时的状态数据等,这类数据一般是难以恢复的,对可用性要求较高,多应用于高用集群;
并行处理:通常涉及大量中间计算结果需要共享;
事件处理:分布式缓存提供了针对事件流的连续查询 (continuous query) 处理技术,满足实时性需求;
极限事务处理:分布式缓存为事务型应用提供高吞吐率、低延时的解决方案,支持高并发事务请求处理,多应用于铁路、金融服务和电等领域。
通过配置合理容量的分布式缓存,能在提升应用数据访问性能的同时,降低总体拥有成本,因此架构设计上做到合理利用缓存,变得就越来越重要。
分布式文件系统
分布式文件系统,是指允许文件通过网络在多台计算机中分散存储的共享文件系统,它通过网络将分布在不同区域的多台计算机连接在一起从而组合成容量更大,处理能力更强的分布式文件存储系统。分布式文件系统很好的解决单台计算机存储和处理能力存在上限的问题,具备大的存储容量和处理能力的扩展能力。
在分布式文件系统中,客户端并非直接访问底层存储,而是通过网络以特定的通信协议和服务器沟通,进而完成文件存储操作,借由通信协议的设计,可以让客户端和服务器端都能根据访问控制清单或是授权,来限制对于文件系统的访问。由于分布式文件系统的网络接入特性,使得不同用户都能通过网络接入分布式文件系统,实现文件数据的共享,从用户角度看,分布式文件系统与单机文件系统看到的视图是一样的,使用方式也相同。
分布式文件系统除了能提供比单机文件系统更大的存储容量和处理能力外,通过数据复制与容错,也拥有了本地文件系统所无法具备的数据备份、数据安全等优点,也就是说,即使系统中有一小部分的节点脱机,整体来说系统仍然可以持续运作而不会有数据损失。
分布式数据库
随着数据量的高速增长,分布式数据库技术也得到了快速的发展,传统的关系型数据库开始从集中式模型向分布式架构发展。分布式数据库是指利用高速计算机网络将物理上分散的多个数据存储单元连接起来组成一个逻辑上统一的数据库。分布式数据库的基本思想是将原来集中式数据库中的数据分散存储到多个通过网络连接的数据存储节点上,以获取更大的存储容量和更高的并发访问量。
分布式数据库可以按需增加、减少数据库处理节点,很好地解决了单台数据库的存储和处理能力上限问题;除此之外,分布式数据库往往具备较强的容错能力,通过数据复制、副本冗余,当某个处理节点出现故障时,能够自动恢复,对使用者没有感知。最后,分布式数据库底层采用廉价服务器,相比传统单机数据库使用的高端服务器和高端存储,成本大幅度降低。
在分布式数据库中,客户端并非直接访问底层的存储系统,而是使用结构化查询语言(SQL语言)访问数据库节点,再由数据库节点的 SQL 引擎翻译成针对底层存储系统的操作。一套分布式数据库往往会服务多个业务,分布式数据库内部支持多个业务之间的隔离,当某个业务出现异常时,只会影响该业务,不会对其它业务造成影响。蚂蚁的 OceanBase 是一个典型的分布式数据库。使用分布式数据库,将大大降低单库故障的发生机率,并简化单库故障的恢复机制。再结合分布式数据中间件在业务维度数据分片和路由、数据源动态调整、应用层高可用容灾、异构数据源适配等能力,将能很好的实现分布式环境下 DB 的高性能访问和高可用。
分布式数据中间件
关系型数据库是经典的持久化解决方案,但在海量业务场景下也会遇到单表的容量瓶颈和单库的性能瓶颈。按照分布式的思想,数据也要拆分,让集中在单点的读写访问分布到多个 DB 服务器上,从而获得容量和性能上的弹性延伸能力。根据业务场景可分为垂直拆分(按业务)、水平拆分(按请求/用户做哈希,或者做区间拆分)、读写拆分等。
按照垂直拆分后的数据会造成本来单库事务变成跨库事务,很难通过传统数据库的机制来保证 ACID,需要引入一种应用层的,基于服务的事务协调机制。这个下文会有专门阐述。按照水平拆分的数据,需要按照某些业务维度将数据拆分到不同的库表,从而解决数据水平扩展的问题,也提升了整体的 QPS,使数据层具备 TB+/天 的吞吐能力。数据拆分后,每次数据访问需要按照分库分表的规则进行路由,本质上是通过规则对原始 SQL 进行重写,拆分成多条 SQL,并分发给链接多个库的数据源去执行,并汇总结果。由于整个过程都在应用层进行,为屏蔽复杂性,需要将上述逻辑封装一层中间件类库中,并通过标准 JDBC 接口对外暴露接口,让业务透明的完成整个数据库的访问过程。
通过一层统一的分布式数据中间件,让业务通过标准 SQL 和标准 JDBC 接口就能访问一组理论容量「无限大」的关系型数据库集群,并且具有很好的性能。同时,通过这层中间件,也能实现所有数据库参数的动态调整、路由规则动态调整、数据库分片权重的调整,从而在应用层实现数据库访问的高可用,且不依赖于数据库高可用机制,甚至提供更灵活强大的故障容忍能力。此外,标准 SQL 和 JDBC 屏蔽了底层数据库的实现,使应用具备透明对接异构数据源的能力,实现复杂场景的业务访问和一些双写双读的需求。
分布式事务
传统关系型数据库的事务模型必须遵守 ACID 原则。在单数据库模式下,ACID 模型能有效保障数据的完整性,但是在大规模分布式环境下,单库无法承载高并发和海量数据,所以数据会被通过垂直拆分或水平拆分到不同的 DB 中,一个业务往往会跨越多个数据库。在 JavaEE 规范中使用 2PC (2 Phase Commit, 两阶段提交) 来处理跨 DB 环境下的事务问题,但是 2PC 是反可伸缩模式,也就是说,在事务处理过程中,参与者需要一直持有资源直到整个分布式事务结束。这样,当业务规模达到千万级以上时,2PC 的局限性就越来越明显,系统可伸缩性会变得很差。基于 BASE 的思想可以仿照 2PC 的方式,在应用层来实现分布式环境下多个事务协调一致。 在充分保障分布式环境下高可用性、高可靠性的同时兼顾数据一致性的要求,其最大的特点是保证数据最终一致 (Eventually consistent)。
在分布式事务中,可以简单分成事务发起方和事务参与者两个角色。发起方负责启动分布式事务,触发创建主事务记录。发起方是分布式事务的总体协调者,负责调用参与者的服务,记录相应的事务日志,并感知整个分布式事务状态来决定整个事务是 COMMIT 还是 ROLLBACK。参与者是分布式事务中的一个原子单位,所有参与者都必须定义 prepare、commit、rollback 3个基本接口,并保证其业务数据的幂等性,也必须保证 prepare 中的数据操作能够被提交 (COMMIT) 或者回滚 (ROLLBACK)。
整个协调过程从发起方开启一个本地事务开始,过程中参与者会显式调用参与方的业务方法,这些显式调用的方法即为一阶段的 prepare 方法,一般参与方会在该方法内锁定资源。这些方法调用可以采用第一小节提到的「消息中间件」,也可以采用「RPC 通讯」。一旦这次本地事务成功,则本地事务提交,发起方会调用第二阶段的真正 commit 方法,来让所有参与方的事务完成第二阶段的提交。本地事务提交失败或者在这之前某个逻辑中失败(可能是本地逻辑异常,也可能参与方一阶段失败),则本地事务失败,发起方会负责完成一阶段的所有回滚操作,避免出现不一致情况。
在分布式环境下,单点故障是个常态,所以无论是发起方还是参与方都可能在任何一刻不可用,此时需要一个第三方的恢复系统来感知所有的事务状态,并通过定期轮询来发现异常状态的事务记录,并将其恢复至最终一致。分布式事务的详细介绍可以参考《分布式事务综述》
在下一篇《分布式系统架构技术分析(二)》中,将从分布式系统架构的容量规划和容灾管控两个方面进行分析,帮助读者更好的理解分布式系统架构。