在极客时间上我订阅了陈皓开设的《左耳听风》专栏,其中有针对分布式架构的一系列文章,在粗略的学习之后,感触颇多,这里对主要的知识点进行简单的总结,作为后续在这方面学习时的纲要。
- 引自原文章:
最近几年,我们一直在谈论各式各样的架构,如高并发架构、异地多活架构、容器化架构、微服务架构、高可用架构、弹性化架构等。还有和这些架构相关的管理型的技术方法,如 DevOps、应用监控、自动化运维、SOA 服务治理、去 IOE 等。面对这么多纷乱的技术,我看到很多团队或是公司都是一个一个地去做这些技术,非常辛苦,也非常累。这样的做法就像我们在撑开一张网里面一个一个的网眼。
其实,只要我们能够找到这张网的“纲”,我们就能比较方便和自如地打开整张网了。那么,这张“分布式大网”的总线——“纲”在哪里呢?我希望通过这一系列文章可以让你找到这个“纲”,从而能让你更好更有效率地做好架构和工程。- 目录:
一、分布式系统架构的冰与火
二、分布式系统的难点
三、分布式系统的技术栈
四、分布式系统关键技术:全栈监控
五、分布式系统关键技术:服务调度
六、分布式系统关键技术:流量与数据调度
七、洞悉PaaS平台的本质
总结
一、分布式系统架构的冰与火
使用分布式架构的主要原因
- 增大系统容量。我们的业务量越来越大,而要能应对越来越大的业务量,一台机器的性能已经无法满足了,我们需要多台机器才能应对大规模的应用场景。所以,我们需要垂直或是水平拆分业务系统,让其变成一个分布式的架构。
- 加强系统可用。我们的业务越来越关键,需要提高整个系统架构的可用性,这就意味着架构中不能存在单点故障。这样,整个系统不会因为一台机器出故障而导致整体不可用。所以,需要通过分布式架构来冗余系统以消除单点故障,从而提高系统的可用性。
单体应用vs分布式优缺点对比
二、分布式系统的难点
异构系统的不标准问题
- 软件和应用不标准。
- 通讯协议不标准。
- 数据格式不标准。
- 开发和运维的过程和方法不标准。
系统架构中的服务依赖性问题
- 如果非关键业务被关键业务所依赖,会导致非关键业务变成一个关键业务。
- 服务依赖链中,出现“木桶短板效应”——整个 SLA 由最差的那个服务所决定。
故障发生的概率更大
- 分布式系统中,虽然故障的影响面可以被隔离,但是因为机器和服务多,出故障的频率也会多。另一方面,因为管理复杂,而且没人知道整个架构中有什么,所以非常容易犯错误。
- 出现故障不可怕,故障恢复时间过长才可怕。
- 出现故障不可怕,故障影响面过大才可怕。
多层架构的运维复杂度更大
- 通常来说,我们可以把系统分成四层:基础层、平台层、应用层和接入层。
- 对于这四层,我们需要知道:
- 任何一层的问题都会导致整体的问题;
- 没有统一的视图和管理,导致运维被割裂开来,造成更大的复杂度。
三、分布式系统的技术栈
关键的两件事
- 大流量处理:通过集群技术把大规模并发请求的负载分散到不同的机器上。
- 目的:提高整体架构的吞吐量,服务更多的并发和流量
- 关键业务保护:提高后台服务的可用性,把故障隔离起来阻止多米诺骨牌效应(雪崩效应)。如果流量过大,需要对业务降级,以保护关键业务流转。
- 目的:提高系统的稳定性,让系统的可用性更高。
提高架构的性能
提高架构的稳定性
分布式系统的关键技术
- 服务治理:服务拆分、服务调用、服务发现,服务依赖,服务的关键度定义……服务治理的最大意义是需要把服务间的依赖关系、服务调用链,以及关键的服务给梳理出来,并对这些服务进行性能和可用性方面的管理。
- 架构软件管理:服务之间有依赖,而且有兼容性问题,所以,整体服务所形成的架构需要有架构版本管理、整体架构的生命周期管理,以及对服务的编排、聚合、事务处理等服务调度功能。
- DevOps:分布式系统可以更为快速地更新服务,但是对于服务的测试和部署都会是挑战。所以,还需要 DevOps 的全流程,其中包括环境构建、持续集成、持续部署等。
- 自动化运维:有了 DevOps 后,我们就可以对服务进行自动伸缩、故障迁移、配置管理、状态管理等一系列的自动化运维技术了。
- 资源调度管理:应用层的自动化运维需要基础层的调度支持,也就是云计算 IaaS 层的计算、存储、网络等资源调度、隔离和管理。
- 整体架构监控:如果没有一个好的监控系统,那么自动化运维和资源调度管理只可能成为一个泡影,因为监控系统是你的眼睛。没有眼睛,没有数据,就无法进行高效的运维。所以说,监控是非常重要的部分。这里的监控需要对三层系统(应用层、中间件层、基础层)进行监控。
- 流量控制:最后是我们的流量控制,负载均衡、服务路由、熔断、降级、限流等和流量相关的调度都会在这里,包括灰度发布之类的功能也在这里。
分布式系统核心
四、分布式系统关键技术:全栈监控
监控系统需要的功能
- 全栈监控;
- 关联分析;
- 跨系统调用的串联;
- 实时报警和自动处置;
- 系统性能分析。
多层体系的监控
- 基础层:监控主机和底层资源。比如:CPU、内存、网络吞吐、硬盘 I/O、硬盘使用等。
- 中间层:就是中间件层的监控。比如:Nginx、Redis、ActiveMQ、Kafka、MySQL、Tomcat 等。
- 应用层:监控应用层的使用。比如:HTTP 访问的吞吐量、响应时间、返回码,调用链路分析,性能瓶颈,还包括用户端的监控。
监控的标准化:
- 日志数据结构化;
- 监控数据格式标准化;
- 统一的监控平台;
- 统一的日志分析。
两大场景
- “体检”
- 容量管理:提供一个全局的系统运行时数据的展示,可以让工程师团队知道是否需要增加机器或者其它资源。
- 性能管理:可以通过查看大盘,找到系统瓶颈,并有针对性地优化系统和相应代码。
- “急诊”
- 定位问题:可以快速地暴露并找到问题的发生点,帮助技术人员诊断问题。
- 性能分析:当出现非预期的流量提升时,可以快速地找到系统的瓶颈,并可以帮助开发人员深入代码。
监控系统实现
- 服务调用链跟踪
- 从对外的 API 开始,然后将后台的实际服务给关联起来,再将这个服务的依赖服务给关联起来,直到最后一个服务(如 MySQL 或 Redis),这样就可以把整个系统的服务全部都串连起来了。这个事情的最佳实践是 Google Dapper 系统,其对应于开源的实现是 Zipkin。对于 Java 类的服务,我们可以使用字节码技术进行字节码注入,做到代码无侵入式。
- 服务调用时长分布
- 使用 Zipkin, 可以看到一个服务调用链上的时间分布,这样有助于我们知道最耗时的服务是什么。
- 服务的 TOP N 视图
- 所谓 TOP N 视图就是一个系统请求的排名情况。一般来说,这个排名会有三种排名的方法:1.按调用量排名,2.按请求最耗时排名,3.按热点排名(一个时间段内的请求次数的响应时间和)。
- 数据库操作关联
- 对于 Java 应用,我们可以很方便地通过 JavaAgent 字节码注入技术拿到 JDBC 执行数据库操作的执行时间。对此,我们可以和相关的请求对应起来。
- 服务资源跟踪
- 我们的服务可能运行在物理机上,也可能运行在虚拟机里,还可能运行在一个 Docker 的容器里,Docker 容器又运行在物理机或是虚拟机上。我们需要把服务运行的机器节点上的数据(如 CPU、MEM、I/O、DISK、NETWORK)关联起来。
五、分布式系统关键技术:服务调度
服务治理上的一些关键点
- 服务关键程度
- 服务依赖关系
- 服务发现
- 整个架构的版本管理
- 服务应用生命周期全管理
服务关键程度和服务的依赖关系
- 微服务是服务依赖最优解的上限,而服务依赖的下限是千万不要有依赖环。
- 解决服务依赖环的方案一般是,依赖倒置的设计模式。在分布式架构上,你可以使用一个第三方的服务来解决这个事。比如,通过订阅或发布消息到一个消息中间件,或是把其中的依赖关系抽到一个第三方的服务中,然后由这个第三方的服务来调用这些原本循环依赖的服务。
- 服务的依赖关系是可以通过技术的手段来梳理的,比如Zipkin
服务状态和生命周期的管理
- 需要有一个服务注册中心,来知道这么几个事
- 整个架构中有多少种服务?
- 这些服务的版本是什么样的?
- 每个服务的实例数有多少个,它们的状态是什么样的?
- 每个服务的状态是什么样的?是在部署中,运行中,故障中,升级中,还是在回滚中,伸缩中,或者是在下线中……
- 服务的生命周期
- Provision,代表在供应一个新的服务;
- Ready,表示启动成功了;
- Run,表示通过了服务健康检查;
- Update,表示在升级中;
- Rollback,表示在回滚中。
- Scale,表示正在伸缩中(可以有 Scale-in 和 Scale-out 两种)。
- Destroy,表示在销毁中。
- Failed,表示失败状态。
整个架构的版本管理
- 除了各个项目的版本管理之外,还需要在上面再盖一层版本管理,如果我们要回滚一个服务的版本,就可以把与之有版本依赖的服务也一起回滚掉。
- 要做到这个事,你需要一个架构的 manifest,一个服务清单,这个服务清单定义了所有服务的版本运行环境,其中包括但不限于:
- 服务的软件版本;
- 服务的运行环境——环境变量、CPU、内存、可以运行的结点、文件系统等;
- 服务运行的最大最小实例数。
资源 / 服务调度
- 服务状态的维持和拟合。
- 服务的弹性伸缩和故障迁移。
- 作业和应用调度。
- 作业工作流编排。
- 服务编排。
服务状态的维持和拟合
- 服务运行过程中,状态也是会有变化的,这样的变化有两种
- 一种是不预期的变化。比如,服务运行因为故障导致一些服务挂掉,或是别的什么原因出现了服务不健康的状态。而一个好的集群管理控制器应该能够强行维护服务的状态。在健康的实例数变少时,控制器会把不健康的服务给摘除,而又启动几个新的,强行维护健康的服务实例数。
- 另外一种是预期的变化。比如,我们需要发布新版本,需要伸缩,需要回滚。这时,集群管理控制器就应该把集群从现有状态迁移到另一个新的状态。这个过程并不是一蹴而就的,集群控制器需要一步一步地向集群发送若干控制命令。这个过程叫“拟合”——从一个状态拟合到另一个状态,而且要穷尽所有的可能,玩命地不断地拟合,直到达到目的。
服务的弹性伸缩和故障迁移
- 服务伸缩
- 底层资源的伸缩;
- 服务的自动化部署;
- 服务的健康检查;
- 服务发现的注册;
- 服务流量的调度。
- 故障迁移
- 宠物模式:就是一定要救活,主要是对于 stateful 的服务。
- 需要:服务的重新启动和服务的监控报警(如果重试恢复不成功,需要人工介入)
- 奶牛模式:就是不救活了,重新生成一个实例。
- 需要:服务的资源申请,服务的自动化部署,服务发现的注册,以及服务的流量调度。
- 宠物模式:就是一定要救活,主要是对于 stateful 的服务。
服务工作流和编排
- 传统的 SOA 通过 ESB(Choreography机制)
- 微服务中使用 API Gateway 或一个简单的消息队列来做相应的编排工作(Orchestration机制)
六、分布式系统关键技术:流量与数据调度
流量调度的主要功能
- 应具备的功能
- 依据系统运行的情况,自动地进行流量调度,在无需人工干预的情况下,提升整个系统的稳定性;
- 让系统应对爆品等突发事件时,在弹性计算扩缩容的较长时间窗口内或底层资源消耗殆尽的情况下,保护系统平稳运行。
- 扩展能力
- 服务流控:服务发现、服务路由、服务降级、服务熔断、服务保护等。
- 流量控制:负载均衡、流量分配、流量控制、异地灾备(多活)等。
- 流量管理:协议转换、请求校验、数据缓存、数据计算等。
流量调度的关键技术
- 高性能:API Gateway 必须使用高性能的技术,所以,也就需要使用高性能的语言。
- 扛流量:要能扛流量,就需要使用集群技术。集群技术的关键点是在集群内的各个结点中共享数据。这就需要使用像 Paxos、Raft、Gossip 这样的通讯协议。因为 Gateway 需要部署在广域网上,所以还需要集群的分组技术。
- 业务逻辑:API Gateway 需要有简单的业务逻辑,所以,最好是像 AWS 的 Lambda 服务一样,可以让人注入不同语言的简单业务逻辑。
- 服务化:一个好的 API Gateway 需要能够通过 Admin API 来不停机地管理配置变更的,而不是通过一个.conf 文件来人肉地修改配置。
状态数据调度
- 一般来说,我们会通过“转移问题”的方法来让服务变成“无状态的服务”。也就是说,会把这些有状态的东西存储到第三方服务上,比如 Redis、MySQL、ZooKeeper,或是 NFS、Ceph 的文件系统中。
分布式事务一致性的问题
- 要解决数据不丢的问题,只能通过数据冗余的方法,就算是数据分区,每个区也需要进行数据冗余处理。这就是数据副本。当出现某个节点的数据丢失时,可以从副本读到。数据副本是分布式系统解决数据丢失异常的唯一手段。简单来说:
- 要想让数据有高可用性,就得写多份数据。
- 写多份的问题会导致数据一致性的问题。
- 数据一致性的问题又会引发性能问题。
- 技术方案:
- Master-Slave 方案
- Master-Master 方案
- 两阶段和三阶段提交方案
- Paxos 方案
七、洞悉PaaS平台的本质
一家商业公司的软件工程能力主要体现在三个地方
- 提高服务的 SLA:能提供多少个 9 的系统可用性
- 高可用的系统
- 自动化的运维
- 能力和资源重用或复用
- 软件模块的重用
- 软件运行环境和资源的重用
- 过程的自动化
- 软件生产流水线
- 软件运维自动化
- 软件生产流水线
PaaS 平台的总体架构
- 调度层 – 主要是 PaaS 的自动化和分布式对于高可用高性能的管理。
- 能力服务层 – 主要是 PaaS 真正提供给用户的服务和能力。
- 流量调度 – 主要是与流量调度相关的东西,包括对高并发的管理。
- 运营管理 – 软件资源库、软件接入、认证和开放平台门户。
- 运维管理 – 主要是 DevOps 相关的东西。
PaaS 平台的生产和运维
总结
构建分布式系统,我们面临的主要问题:
- 分布式系统的硬件故障发生率更高,故障发生是常态,需要尽可能地将运维流程自动化。
- 需要良好地设计服务,避免某服务的单点故障对依赖它的其他服务造成大面积影响。
- 为了容量的可伸缩性,服务的拆分、自治和无状态变得更加重要,可能需要对老的软件逻辑做大的修改。
- 老的服务可能是异构的,此时需要让它们使用标准的协议,以便可以被调度、编排,且互相之间可以通信。
- 服务软件故障的处理也变得复杂,需要优化的流程,以加快故障的恢复。
- 为了管理各个服务的容量,让分布式系统发挥出最佳性能,需要有流量调度技术。
- 分布式存储会让事务处理变得复杂;在事务遇到故障无法被自动恢复的情况下,手动恢复流程也会变得复杂。
- 测试和查错的复杂度增大。
- 系统的吞吐量会变大,但响应时间会变长。
解决方案:
- 需要有完善的监控系统,以便对服务运行状态有全面的了解。
- 设计服务时要分析其依赖链;当非关键服务故障时,其他服务要自动降级功能,避免调用该服务。
- 重构老的软件,使其能被服务化;可以参考 SOA 和微服务的设计方式,目标是微服务化;使用 Docker 和 Kubernetes 来调度服务。
- 为老的服务编写接口逻辑来使用标准协议,或在必要时重构老的服务以使得它们有这些功能。
- 自动构建服务的依赖地图,并引入好的处理流程,让团队能以最快速度定位和恢复故障,详见《故障处理最佳实践:应对故障》一文。
- 使用一个 API Gateway,它具备服务流向控制、流量控制和管理的功能。
- 事务处理建议在存储层实现;根据业务需求,或者降级使用更简单、吞吐量更大的最终一致性方案,或者通过二阶段提交、Paxos、Raft、NWR 等方案之一,使用吞吐量小的强一致性方案。
- 通过更真实地模拟生产环境,乃至在生产环境中做灰度发布,从而增加测试强度;同时做充分的单元测试和集成测试以发现和消除缺陷;最后,在服务故障发生时,相关的多个团队同时上线自查服务状态,以最快地定位故障原因。
- 通过异步调用来减少对短响应时间的依赖;对关键服务提供专属硬件资源,并优化软件逻辑以缩短响应时间。