本篇文章主要源自本人自学左耳朵耗子的专栏中的相关分布式的文章归纳而来。
https://coolshell.cn/ 该专栏作者自己的博客,推荐一下
目录
什么是分布式架构
相对单机而言,分布式至少是多机部署,多机共同分担任务处理(源自一位网友)
为什么要使用分布式架构
分布式架构的优点
- 增大系统容量:垂直或是水平拆分业务系统,让其变成一个分布式的架构
- 加强系统可用:通过分布式架构来冗余系统以消除单点故障,从而提高系统的可用性
- 模块重用度高
- 软件服务模块被拆分,开发和发布速度可以并行而变得更快
- 系统扩展性更高
- 团队协作流程改善
分布式架构的缺点
- 架构设计变得复杂(尤其是其中的分布式事务)
- 部署单个服务会比较快,但是如果一次部署需要多个服务,流程会变得复杂
- 系统的吞吐量会变大,但是响应时间会变长
- 运维复杂度会因为服务变多而变得很复杂
- 架构复杂导致学习曲线变大
- 测试和查错的复杂度增大
- 技术多元化,这会带来维护和运维的复杂度
- 管理分布式系统中的服务和调度变得困难和复杂
单体应用和分布式架构的比较
分布式架构解决了什么问题
- 大流量处理:通过集群技术把大规模并发请求的负载分散到不同的机器上
- 关键业务保护:提高后台服务的可用性,把故障隔离起来阻止多米诺骨牌效应(雪崩效应)。如果流量过大,需要对业务降级,以保护关键业务流转
分布式系统的发展
- 20 世纪 90 年代前,是单体架构,软件模块高度耦合
- 2000 年左右出现了比较松耦合的 SOA 架构:一个标准的协议或是中间件来联动其它相关联的服务(如 ESB);服务间并不直接依赖,而是通过中间件的标准协议或是通讯框架相互依赖
- 2010 年后,出现了微服务架构:松耦合;每一个微服务都能独立完整地运行(自包含);数据库根据服务分库;
微服务的意义:微服务的出现使得开发速度更快,部署快,隔离性高,系统的扩展度好,但是在集成测试、运维和服务管理等方面比较麻烦。所以,需要一套比较好的微服务 PaaS 平台。就像 Spring Cloud 一样需要提供各种配置服务、服务发现、智能路由、控制总线……还有像 Kubernetes 提供的各式各样的部署和调度方式。
分布式系统需要注意的问题
异构系统的不标准问题
- 软件和应用不标准
- 通讯协议不标准
- 数据格式不标准
- 开发和运维的过程和方法不标准
系统架构中的服务依赖性问题
- 如果非关键业务被关键业务所依赖,会导致非关键业务变成一个关键业务
- 服务依赖链中,出现“木桶短板效应”——整个 SLA 由最差的那个服务所决定
解决思路:
- 定义出服务的关键程度
- 服务调用的主要路径
- 数据库方面也需要做相应的隔离,一个业务线用一套自己的数据库
故障发生的概率更大
- 故障恢复时间过长
- 故障影响面过大
解决思路:
- 监控关键指标
- 自动化的方式恢复故障,减少故障影响面
多层架构的运维复杂度更大
- 任何一层的问题都会导致整体的问题
- 没有统一的视图和管理,导致运维被割裂开来,造成更大的复杂度
- 按技能分工导致各管各的,分工后的协作是否统一和规范
分布式架构层级
- 基础层:机器、网络和存储设备等
- 平台层:中间件层,Tomcat、MySQL、Redis、Kafka 之类的软件
- 应用层:业务软件,比如,各种功能的服务
- 接入层:接入用户请求的网关、负载均衡或是 CDN、DNS等
配置管理
- 底层和操作系统相关:底层和中间层是不能让用户灵活修改的,而是只让用户选择
- 中间层和中间件相关
- 最上面和业务应用相关
分布式架构技术栈
提高架构的性能
- 缓存系统:对于分布式系统下的缓存系统,需要的是一个缓存集群。这其中需要一个 Proxy 来做缓存的分片和路由。
- 负载均衡系统:水平扩展的关键技术,它可以使用多台机器来共同分担一部分流量请求
- 异步调用:主要通过消息队列来对请求做排队处理,把前端的请求的峰值给“削平”,后端通过能够处理的速度来处理请求。这样可以增加系统的吞吐量,但是实时性较差。同时,还会引入消息丢失的问题,所以要对消息做持久化,这会造成“有状态”的结点,从而增加了服务调度的难度
- 数据分区和数据镜像:数据分区是把数据按一定的方式分成多个区(比如通过地理位置),不同的数据区来分担不同区的流量。需要一个数据路由的中间件,会导致跨库的 Join 和跨库的事务非常复杂。数据镜像是把一个数据库镜像成多份一样的数据,不需要数据路由的中间件。可以在任意结点上进行读写,内部会自行同步数据。数据镜像中最大的问题就是数据的一致性问题
提高架构的稳定性
- 服务拆分:一是为了隔离故障,二是为了重用服务模块。但服务拆分完之后,会引入服务调用间的依赖问题。
- 服务冗余:为了去除单点故障,并可以支持服务的弹性伸缩,以及故障迁移。弹性伸缩时,需要考虑数据的复制或是重新分片,迁移的时候还要迁移数据到其它机器上
- 限流降级:当系统实在扛不住压力时,只能通过限流或者功能降级的方式来停掉一部分服务,或是拒绝一部分用户,以确保整个架构不会挂掉
- 高可用架构:通常来说高可用架构是从冗余架构的角度来保障可用性。比如,多租户隔离,灾备多活,或是数据可以在其中复制保持一致性的集群。总之,就是为了不出单点故障
- 高可用运维:高可用运维指的是 DevOps 中的 CI/CD(持续集成 / 持续部署)。一个良好的运维应该是一条很流畅的软件发布管线,其中做了足够的自动化测试,还可以做相应的灰度发布,以及对线上系统的自动化控制。这样,可以做到“计划内”或是“非计划内”的宕机事件的时长最短。
分布式系统的关键技术
- 服务治理:服务拆分、服务调用、服务发现、服务依赖、服务的关键度定义;最大意义是需要把服务间的依赖关系、服务调用链,以及关键的服务给梳理出来,并对这些服务进行性能和可用性方面的管理
- 架构软件管理:服务之间有依赖,而且有兼容性问题,所以,整体服务所形成的架构需要有架构版本管理、整体架构的生命周期管理,以及对服务的编排、聚合、事务处理等服务调度功能。
- DevOps:包括环境构建、持续集成、持续部署等
- 自动化运维:对服务进行自动伸缩、故障迁移、配置管理、状态管理等一系列的自动化运维技术
- 资源调度管理:计算、存储、网络等资源调度、隔离和管理
- 整体架构监控:监控需要对三层系统(应用层、中间件层、基础层)进行监控
- 流量控制:负载均衡、服务路由、熔断、降级、限流等和流量相关的调度,包括灰度发布之类的功能
分布式系统核心
- 全栈系统监控
- 服务 / 资源调度
- 状态和数据调度
- 流量调度
全栈监控
全栈监控需要完成的功能
- 全栈监控
- 关联分析
- 跨系统调用的串联
- 实时报警和自动处置
- 系统性能分析
多层体系的监控
- 基础层:监控主机和底层资源-CPU、内存、网络吞吐、硬盘 I/O、硬盘使用等
- 中间层:就是中间件层的监控-Nginx、Redis、ActiveMQ、Kafka、MySQL、Tomcat 等
- 应用层:监控应用层的使用-HTTP 访问的吞吐量、响应时间、返回码、调用链路分析、性能瓶颈,还包括用户端的监控
监控的标准化
- 日志数据结构化
- 监控数据格式标准化
- 统一的监控平台
- 统一的日志分析
什么才是好的监控系统
两个问题:
- 监控数据是隔离开来的:因为公司分工的问题,开发、应用运维、系统运维,各管各的,所以很多公司的监控系统之间都有一道墙,完全串不起来
- 监控的数据项太多:信息太多等于没有信息
好的监控系统应该有以下几个特征:
- 关注于整体应用的 SLA:主要从为用户服务的 API 来监控整个系统
- 关联指标聚合:主要是三层系统数据:基础层、平台中间件层和应用层。其中,最重要的是把服务和相关的中间件以及主机关联在一起。无论运行在哪里,我们都需要把服务的具体实例和主机关联在一起,否则,对于一个分布式系统来说,定位问题犹如大海捞针。
- 快速故障定位:速定位问题需要对整个分布式系统做一个用户请求跟踪的 trace 监控,需要监控到所有的请求在分布式系统中的调用链,最好做成没有侵入性的
针对两个场景设计:
“体检”:
- 容量管理-提供一个全局的系统运行时数据的展示,可以让工程师团队知道是否需要增加机器或者其它资源;
- 性能管理-可以通过查看大盘,找到系统瓶颈,并有针对性地优化系统和相应代码
“急诊”:
- 定位问题-可以快速地暴露并找到问题的发生点,帮助技术人员诊断问题
- 性能分析-当出现非预期的流量提升时,可以快速地找到系统的瓶颈,并帮助开发人员深入代码
如何做出一个好的监控系统
- 服务调用链跟踪:从对外的 API 开始,将后台的实际服务给关联起来,然后再进一步将这个服务的依赖服务关联起来,直到最后一个服务(如 MySQL 或 Redis),这样就可以把整个系统的服务全部都串连起来。最佳实践是 Google Dapper 系统,其对应于开源的实现是 Zipkin。对于 Java 类的服务,可以使用字节码技术进行字节码注入,做到代码无侵入式。
- 服务调用时长分布:使用 Zipkin等开源工具,可以看到一个服务调用链上的时间分布,这样有助于我们知道最耗时的服务是什么。
- 服务的 TOP N 视图:TOP N 视图就是一个系统请求的排名情况。三种排名的方法:a)按调用量排名,b) 按请求最耗时排名,c)按热点排名(一个时间段内的请求次数的响应时间和)。
- 数据库操作关联:对于 Java 应用,可以通过 JavaAgent 字节码注入技术拿到 JDBC 执行数据库操作的执行时间。可以和相关的请求对应起来
- 服务资源跟踪:把服务运行的机器节点上的数据(如 CPU、MEM、I/O、DISK、NETWORK)关联起来
服务调度
服务调度关键技术
- 服务关键程度
- 服务依赖关系
- 服务发现
- 整个架构的版本管理
- 服务应用生命周期全管理
服务关键程度和服务的依赖关系
- 梳理和定义服务的重要程度
- 梳理出服务间的依赖关系
微服务是服务依赖最优解的上限,而服务依赖的下限是千万不要有依赖环
解决服务依赖环的方案一般是,依赖倒置的设计模式。在分布式架构上,你可以使用一个第三方的服务来解决。比如,通过订阅或发布消息到一个消息中间件,或是把其中的依赖关系抽到一个第三方的服务中,然后由这个第三方的服务来调用这些原本循环依赖的服务。
服务的依赖关系是可以通过技术的手段来发现的,Zipkin
服务状态和生命周期的管理
- 服务注册中心
- 整个架构中有多少种服务
- 这些服务的版本是什么样的
- 各个服务的实例数有多少个,它们的状态是什么样的
- 每个服务的状态是什么样的
服务的状态
- Provision,代表在供应一个新的服务
- Ready,表示启动成功了
- Run,表示通过了服务健康检查
- Update,表示在升级中
- Rollback,表示在回滚中
- Scale,表示正在伸缩中(可以有 Scale-in 和 Scale-out 两种)
- Destroy,表示在销毁中
- Failed,表示失败状态
整个架构的版本管理
由一堆服务的版本集所形成的整个架构的版本控制,每一次对这个清单的变更都需要被记录下来,算是一个架构的版本管理,集群控制系统需要能够解读并执行这个清单中的变更,以操作和管理整个集群中的相关变更。
- 服务的软件版本
- 服务的运行环境:环境变量、CPU、内存、可以运行的结点、文件系统等
- 服务运行的最大最小实例数
资源 / 服务调度
关键技术:
- 服务状态的维持和拟合
- 服务的弹性伸缩和故障迁移
- 作业和应用调度
- 作业工作流编排
- 服务编排
弹性伸缩:
- 底层资源的伸缩
- 服务的自动化部署
- 服务的健康检查
- 服务发现的注册
- 服务流量的调度
故障迁移:
- 宠物模式-所谓宠物模式,就是一定要救活,主要是对于 stateful 的服务;服务的重新启动和服务的监控报警(如果重试恢复不成功,需要人工介入)
- 奶牛模式-奶牛模式,就是不救活了,重新生成一个实例;服务的资源申请,服务的自动化部署,服务发现的注册,以及服务的流量调度
流量与数据调度
流量调度
主要功能:
- 依据系统运行的情况,自动地进行流量调度,在无需人工干预的情况下,提升整个系统的稳定性
- 让系统应对爆品等突发事件时,在弹性计算扩缩容的较长时间窗口内或底层资源消耗殆尽的情况下,保护系统平稳运行
- 服务流控-服务发现、服务路由、服务降级、服务熔断、服务保护等
- 流量控制-负载均衡、流量分配、流量控制、异地灾备(多活)等
- 流量管理-协议转换、请求校验、数据缓存、数据计算等
关键技术:
- 高性能-API Gateway 必须使用高性能的技术,使用高性能的语言
- 扛流量-要能扛流量,需要使用集群技术。集群技术的关键点是在集群内的各个结点中共享数据。这需要使用像 Paxos、Raft、Gossip 这样的通讯协议。因为 Gateway 需要部署在广域网上,所以还需要集群的分组技术。
- 业务逻辑:API Gateway 需要有简单的业务逻辑
- 服务化:一个好的 API Gateway 需要能够通过 Admin API 来不停机地管理配置变更,而不是通过一个.conf 文件来人肉地修改配置。
状态数据调度:
有些服务会保存一些数据,而这些数据是不能丢失的,所以,这些数据是需要随服务一起调度的
“转移问题”的方法来让服务变成“无状态的服务”,把这些有状态的东西存储到第三方服务上,比如 Redis、MySQL、ZooKeeper,或是 NFS、Ceph 的文件系统中
分布式事务一致性的问题
数据一致性的问题
数据副本:解决数据不丢失的问题,只能通过数据冗余的方法,数据副本是分布式系统解决数据丢失异常的唯一手段
- 要想让数据有高可用性,就得写多份数据
- 写多份会引起数据一致性的问题
- 数据一致性的问题又会引发性能问题
在应用层上解决事务问题,只有“两阶段提交”这样的方式,而在数据层解决事务问题,Paxos 算法则是不二之选;
- Master-Slave 方案
- Master-Master 方案
- 两阶段和三阶段提交方案:很多公司的分布式系统事务基本上都是两阶段提交的变种。比如:阿里推出的 TCC–Try–Confirm–Cancel,或是亚马逊的 Plan–Reserve–Confirm 的方式,等等。凡是通过业务补偿,或是在业务应用层上做的分布式事务的,基本上都是两阶段提交,或是两阶段提交的变种。
- Paxos 方案
数据结点的分布式方案
真正完整解决数据 Scale 问题的应该还是数据结点自身。只有数据结点自身解决了这个问题,才能做到对上层业务层的透明,业务层可以像操作单机数据库一样来操作分布式数据库,这样才能做到整个分布式服务架构的调度。
- AWS 的 Aurora
- MySQL 官方也有 MySQL Cluster
- PingCAP 的 TiDB
- 国外的 CockroachDB
- 阿里的 OceanBase
PaaS平台介绍
软件工程能力主要体现
提高服务的 SLA:
所谓服务的 SLA,也就是我们能提供多少个 9 的系统可用性,而每提高一个 9 的可用性都是对整个系统架构的重新洗礼
- 高可用的系统
- 自动化的运维
能力和资源重用或复用:
需要有两个重要的能力:一个是“软件抽象的能力”,另一个是“软件标准化的能力”。软件抽象就是找出通用的软件模块或服务,软件标准化就是使用统一的软件通讯协议、统一的开发和运维管理方法……这样能让整体软件开发运维的能力和资源得到最大程度的复用,从而增加效率。
- 软件模块的重用
- 软件运行环境和资源的重用
过程的自动化
需要 CI/CD 的 DevOps 式的自动化;需要能够对正在运行的生产环境中的软件进行自动化运维
- 软件生产流水线
- 软件运维自动化
PaaS 平台的本质
一个好的 PaaS 平台应该具有分布式、服务化、自动化部署、高可用、敏捷以及分层开放的特征,并可与 IaaS 实现良好的联动
PaaS 跟传统中间件最大的差别
- 服务化是 PaaS 的本质-软件模块重用,服务治理,对外提供能力是 PaaS 的本质
- 分布式是 PaaS 的根本特性-多租户隔离、高可用、服务编排是 PaaS 的基本特性
- 自动化是 PaaS 的灵魂-自动化部署安装运维,自动化伸缩调度是 PaaS 的关键
PaaS 平台的总体架构
- PaaS 调度层 – 主要是 PaaS 的自动化和分布式对于高可用高性能的管理
- PaaS 能力服务层 – 主要是 PaaS 真正提供给用户的服务和能力
- PaaS 的流量调度 – 主要是与流量调度相关的东西,包括对高并发的管理。
- PaaS 的运营管理 – 软件资源库、软件接入、认证和开放平台门户。
- PaaS 的运维管理 – 主要是 DevOps 相关的东西。
PaaS 平台的生产和运维
- 从左上开始软件构建,进入软件资产库(Docker Registry+ 一些软件的定义),然后走 DevOps 的流程,通过整体架构控制器进入生产环境,生产环境通过控制器操作 Docker+Kubernetes 集群进行软件部署和生产变更。
- 同步服务的运行状态,并通过生命周期管理来拟合状态,如图右侧部分所示。服务运行时的数据会进入到相关应用监控,应用监控中的一些监控事件会同步到生命周期管理中,再由生命周期管理器来做出决定,通过控制器来调度服务运行。当应用监控中心发现流量变化,要进行强制性伸缩时,它通过生命周期管理来通知控制系统进行伸缩。
- 左下是服务接入的相关组件,主要是网关服务,以及 API 聚合编排和流程处理。这对应于之前说过的流量调度和 API Gateway 的相关功能。
总结:
构建分布式系统,面临的主要问题
传统的单体架构系统容量是有上限的。同时,为了应对有计划和无计划的上线时间,系统的可用性也是有极限的。分布式系统为以上两个问题提供了解决方案,并且还附带有其他优势。但是,要同时解决这两个问题决非易事。为了构建分布式系统,我们面临的主要问题如下。
- 分布式系统的硬件故障发生率更高,故障发生是常态,需要尽可能地将运维流程自动化。
- 需要良好地设计服务,避免某服务的单点故障对依赖它的其他服务造成大面积影响。
- 为了容量的可伸缩性,服务的拆分、自治和无状态变得更加重要,可能需要对老的软件逻辑做大的修改。
- 老的服务可能是异构的,此时需要让它们使用标准的协议,以便可以被调度、编排,且互相之间可以通信。
- 服务软件故障的处理也变得复杂,需要优化的流程,以加快故障的恢复。
- 为了管理各个服务的容量,让分布式系统发挥出最佳性能,需要有流量调度技术。
- 分布式存储会让事务处理变得复杂;在事务遇到故障无法被自动恢复的情况下,手动恢复流程也会变得复杂。
- 测试和查错的复杂度增大。
- 系统的吞吐量会变大,但响应时间会变长。
解决方案
- 需要有完善的监控系统,以便对服务运行状态有全面的了解。
- 设计服务时要分析其依赖链;当非关键服务故障时,其他服务要自动降级功能,避免调用该服务。
- 重构老的软件,使其能被服务化;可以参考 SOA 和微服务的设计方式,目标是微服务化;使用 Docker 和 Kubernetes 来调度服务。
- 自动构建服务的依赖地图,并引入好的处理流程,让团队能以最快速度定位和恢复故障。
- 使用一个 API Gateway,它具备服务流向控制、流量控制和管理的功能。
- 事务处理建议在存储层实现;根据业务需求,或者降级使用更简单、吞吐量更大的最终一致性方案,或者通过二阶段提交、Paxos、Raft、NWR 等方案之一,使用吞吐量小的强一致性方案。
- 通过更真实地模拟生产环境,乃至在生产环境中做灰度发布,从而增加测试强度;同时做充分的单元测试和集成测试以发现和消除缺陷;最后,在服务故障发生时,相关的多个团队同时上线自查服务状态,以最快地定位故障原因。
- 通过异步调用来减少对短响应时间的依赖;对关键服务提供专属硬件资源,并优化软件逻辑以缩短响应时间。