1.4 分布式架构的问题

通过前面的介绍,我们了解到为了解决由业务访问量增长和并发场景带来的问题,分布式架构将应用服务部署到了分散的资源上面,从而支持高性能和高可用。我们还从这一过程中总结出了分布式架构的四个特征——分布性、自治性、并行性和全局性,这四个特征密切相关。知道了分布式架构的特征和优势之后,接下来聚焦分布式架构的问题,例如服务如何拆分、分散的服务如何通信及协同,以及如何处理分布式计算、调度和监控。

1.4.1 分布式架构的逻辑结构图

分布式架构按照拆分的原则,将应用分配到不同的物理资源上。借助这个思路,我们对分布式架构遇到的问题进行层层剖析,根据四个特征把问题进一步细化,将其拆分成具体的问题。通过为什么、是什么、怎么办的步骤来掌握分布式结构的核心思想,从而对其进行应用。

既然分布式是从拆分开始的,那我们的问题也从拆分入手。任何一个系统都是为业务服务的,所以首先根据业务特点对应用服务进行拆分,拆分之后会形成一个个服务或者应用。这些服务具有自治性,可以完成自己对应的业务功能,以及拥有单独的资源。当一个服务需要调用其他服务时,需要考虑服务之间通信的问题。同理,多个服务要完成同一件事时,需要考虑协同问题。当遇到大量任务需要进行大量计算工作的时候,需要多个同样的服务共同完成。任何应用或者计算架构都需要考虑存储的问题。实现了对应用与资源的管理和调度,才能实现系统的高性能和可用性。此外,加入指标与监控能够保证系统正常运行。图 1-24 将分布式架构需要解决的问题按照顺序列举为如下几步。

(1) 分布式是用分散的服务和资源代替几种服务和资源,所以先根据业务进行应用服务拆分。

(2) 由于服务分布在不同的服务器和网络节点上,所以要解决分布式调用的问题。

(3) 服务能够互相感知和调用以后,需要共同完成一些任务,这些任务或者共同进行,或者依次进行,因此需要解决分布式协同问题。

(4) 在协同工作时,会遇到大规模计算的情况,需要考虑使用多种分布式计算的算法来应对。

(5) 任何服务的成果都需要保存下来,这就要考虑存储问题。和服务一样,存储的分布式也可以提高存储的性能和可用性,因此需要考虑分布式存储的问题。

(6) 所有的服务与存储都可以看作资源,因此需要考虑分布式资源管理和调度。

(7) 设计分布式架构的目的是实现高性能和可用性。为了达到这个目的,一起来看看高性能与可用性的最佳实践,例如缓存的应用、请求限流、服务降级等。

(8) 最后,系统上线以后需要对性能指标进行有效的监控才能保证系统稳定运行,此时指标与监控就是我们需要关注的问题。

图 1-24 分布式架构需解决问题的结构图

后面我们会根据图 1-24 中箭头指示的顺序,介绍每个问题的具体内容,并将虚线框的部分补齐,也就是将每个问题细化。

1.4.2 应用服务拆分

分布式架构确实解决了高性能、高可用、可扩展、可伸缩等问题。其自治性特征也让服务从系统中独立出来,从业务到技术再到团队都是独立的个体。但在分布式架构的实践过程中,产生了一些争论和疑惑,例如应用服务如何划分?划分以后如何设计?业务的边界如何定义?应用服务如果拆分得过于细致,就会导致系统架构的复杂度增加,项目难以推进,程序员学习曲线变得陡峭;如果拆分得过于粗旷,又无法达到利用分布式资源完成海量请求以及大规模任务的目的。一般来说,应用服务的划分都是由项目中的技术或者业务专家凭借经验进行的。即使有方法论,也是基于经验而得,究其根本,我们还是不清楚业务的边界在什么地方。换言之,定义业务的边界是划分应用服务的关键。说白了,就是先划分业务,再针对划分后的业务进行技术实现。

既然技术的实现来源于业务,那么对业务的分析就需要放在第一位。我们可以利用 DDD(Domain-Driven Design,领域驱动设计)的方法定义领域模型,确定业务和应用服务的边界,最终引导技术的实现。按照 DDD 方法设计出的应用服务符合“高内聚、低耦合”的标准。DDD 是一种专注于复杂领域的设计思想,其围绕业务概念构建领域模型,并对复杂的业务进行分隔,再对分隔出来的业务与代码实践做映射。DDD 并不是架构,而是一种架构设计的方法论,它通过边界划分将业务转化成领域模型,领域模型又形成应用服务的边界,协助架构落地。

这部分内容如下。

领域驱动设计的模型结构:包括领域、领域分类、子域、领域事件、聚合、聚合根、实体和值对象的介绍。

分析业务需求形成应用服务:包括业务场景分析、抽象领域对象、划定限界上下文。

领域驱动设计分层架构:包括分层原则、每层内容和特征,以及分层实例。

1.4.3 分布式调用

服务与资源一旦分散开,要想调用就没有那么简单了。需要针对不同的用户请求,找到对应的服务模块,比如用户下订单就需要调用订单服务。当大量用户请求相同的服务,又存在多个服务的时候,需要根据资源分布将用户请求均匀分配到不同服务上去。就好像用户浏览商品时,有多个商品服务可供选择,那么由其中哪一个提供服务呢?服务之间的调用也是如此,服务如何找到另外一个服务,找到以后通过什么方式调用,都是需要思考的问题。针对调用的问题,在不同架构层面有不同的处理方式:在用户请求经过互联网进入应用服务器之前,需要通过负载均衡和反向代理;在内网的应用服务器之间需要 API 网关调用;服务与服务之间可以通过服务注册中心、消息队列、远程调用等方式互相调用。因此可以将分布式调用总结为两部分,第一部分是感知对方,包括负载均衡、API 网关、服务注册与发现、消息队列;第二部分是信息传递,包括 RPC、RMI、NIO 通信。

负载均衡分类:针对接入层硬件和软件负载均衡的实现原理和算法进行介绍。

API 网关:介绍 API 网关的技术原理和具体功能,并且通过对比的方式介绍流行的 API 网关。

服务注册与发现:介绍相关的原理和概念,以及它与发布/订阅模式的区别。

服务间的远程调用:介绍 RPC 调用过程、RPC 动态代理、RPC 序列化、协议编码和网络传输,还会介绍 Netty 是如何实现 RPC 的。

1.4.4 分布式协同

分布式协同顾名思义就是大家共同完成一件事,而且是一件大事。在完成这件大事的过程中,难免会遇到很多问题。例如,同时响应多个请求的库存服务会对同一商品的库存进行“扣减”,为了保证商品库存这类临界资源的访问独占性,引入了分布式锁的概念,让多个“扣减”请求能够串行执行。又例如,在用户进行“下单”操作时,需要将“记录订单”(订单服务)和“扣减库存”(库存服务)放在事务中处理,要么两个操作都完成,要么都不完成。再例如,对商品表做了读写分离之后,产生了主从数据库,当主库发生故障时,会通过分布式选举的方式选举出新的主库,以替代原来主库的工作。我们将这些问题归纳为以下几点。

分布式系统的特性与互斥问题:集中互斥算法、基于许可的互斥算法、令牌环互斥算法。

分布式锁:分布式锁的由来和定义、缓存实现分布式锁、ZooKeeper 实现分布式锁、分段加锁。

分布式事务:介绍分布式事务的原理和解决方案。包括 CAP、BASE、ACID 等的原理;DTP 模型;2PC、TCC 方案。

分布式选举:介绍分布式选举的几种算法,包括 Bully 算法、Raft 算法、ZAB 算法。

分布式系统的实践:介绍 ZooKeeper 的基本原理和组件。

1.4.5 分布式计算

在大数据和人工智能时代,有海量的信息需要处理,这些信息会经过层层筛选进入系统,最终形成数据。要想让数据产生商业价值,就离不开数据模型和计算。针对海量数据的计算,分布式架构通常采用水平扩展的方式来应对挑战。在不同的计算场景下计算方式会有所不同,计算模式分为两种:针对批量静态数据计算的 MapReduce 模式,以及针对动态数据流进行计算的 Stream 模式。我们会展开介绍这两种模式。

MapReduce 模式:介绍其特点、工作流程和示例。

Stream 模式:通过 Storm 的最佳实践介绍其要素和流程。

1.4.6 分布式存储

简单理解,存储就是数据的持久化。从参与者的角度来看,数据生产者生产出数据,然后将其存储到媒介上,数据使用者通过数据索引的方式消费数据。从数据类型上来看,数据又分为结构化数据、半结构化数据、非结构化数据。在分布式架构中,会对数据按照规则分片,对于主从数据库还需要完成数据同步操作。如果要建立一个好的数据存储方案,需要关注数据均匀性、数据稳定性、节点异构性以及故障隔离几个方面。因此,我们组织以下内容来讲解分布式存储。

数据存储面临的问题和解决思路:RAID 磁盘阵列。

分布式存储概念:分布式存储的要素和数据类型分类。

分布式关系数据库:分表分库、主从复制、数据扩容。

分布式缓存:缓存分片算法、Redis 集群方案、缓存节点之间的通信、请求分布式缓存的路由、缓存节点的扩展和收缩、缓存故障的发现和恢复。

1.4.7 分布式资源管理与调度

如果把每个用户请求都看成系统需要完成的任务,那么分布式架构要做的就是对任务与资源进行匹配。首先,我们会介绍资源调度的过程。然后,通过介绍 Linux Container 让大家了解资源划分和调度策略是如何工作的。最后,介绍三类资源调度的架构,以及 Kubernetes 的最佳实践。具体为如下内容。

分布式调度的由来与过程

资源划分和调度策略

分布式调度架构

•中心化调度的特点是由一个网络节点参与资源的管理和调度。

•两级调度在单体调度的基础上将资源的管理和调度从一层分成了两层,分别是资源管理层和任务分配层。

•共享状态调度,通过共享集群状态、共享资源状态和共享任务状态完成调度工作。

资源调度的实践:介绍 Kubernetes 的架构及其各组件的运行原理。

1.4.8 高性能与可用性

高性能和可用性本身就是分布式架构要达成的目的。分布式架构拆分和分而治之的思想也是围绕着这个目的展开的。这部分主要从缓存、可用性两个方面展开。在分布式架构的每个层面和角度,都可以利用缓存技术提高系统性能。由于技术使用比较分散,在第 8 章中我们会做一个总结性的描述。对于可用性来说,为了保证系统的正常运行会通过限流、降级、熔断等手段进行干涉。本部分的结构如下。

缓存的应用:HTTP 缓存、CDN 缓存、负载均衡缓存、进程内缓存、分布式缓存。

可用性的策略:请求限流、服务降级、服务熔断。

1.4.9 指标与监控

判断一个架构是好是坏时,有两个参考标准,即性能指标和可用性指标,分布式架构也是如此。性能指标又分为吞吐量、响应时间和完成时间。由于系统的分布性,服务会分布到不同的服务器和网络节点,因此监控程序需要在不同的服务器和网络节点上对服务进行监控。在分布式监控中会提到监控系统的分类、分层以及 Zabbix、Prometheus、ELK 的最佳实践。本部分的结构如下。

性能指标:延迟、流量、错误、饱和度。

分布式监控系统:创建监控系统的步骤、监控系统的分类、监控系统的分层。

流行监控系统的最佳实践:包含 Zabbix、Prometheus。