微服务实战读书笔记
January 10, 2022
微服务的定义
三大关键特性
- 每个微服务只负责一个功能
- 每个微服务都有自己的数据存储 // 低耦合,只能通过接口访问对方数据
- 微服务自己负责编排和协作 // 高内聚?
两个基本特性
- 可独立部署 // 非庞大的单体应用
- 可替代
微服务架构原则
- 自治性:松耦合,可独立部署
- 可恢复性:故障隔离,模块故障仅影响微服务自身。 // 是不是叫“隔离”更加合适
- 透明性:每个微服务有完整的日志、监控以及基础设施数据
- 自动化:保障系统频繁部署和运维的正确性,通过引入流水线or云函数
- 一致性:微服务需要考虑向后兼容,确保不需要强迫其他团队升级或者破坏服务之间的复杂交互。
为何选择微服务
- 技术差异性:允许根据服务的特性选择不同的技术栈
- 系统复杂度会逐步增加:不断缩短的交付周期
- 降低重复和风险:灵活性、部署风险
微服务的设计、运维挑战
- 划定微服务范围需要领域知识:如何划分某个服务的职责需要对领域有充分的理解。
- 良好的契约设计 // 多团队设计,需要服务契约保障完整、简洁及可预测(协议定义反映了真实表现)
- 分布式带来的风险:网络、带宽
- 故障点增多:微服务并不能消除风险,而是将这个成本移到了系统生命周期的后半阶段:降低了开发过程中的冲突,但是增加了运维阶段系统部署、验证以及监控的复杂度。
- 运维挑战:难以实现的可观测性、多故障同时出现(需要考虑服务的鲁棒性)
微服务的开发生命周期
- 设计
- 部署
- 复杂的部署过程就是“通下水道”(plumbing)【用来比喻一些价值不大的脏活累活,而不能为客户创造任何价值】。
- 部署需要容器化、流水线化,尽可能的“单调”
- 监控
- 发现潜在的薄弱环节并进行重构
- 重点指标:业务指标、应用日志、运维指标和基础设施指标
- 其他:有责任感和运维意识的工程师文化
康威定律:组织沟通方式会通过系统设计表达出来(沟通成本随着项目或者组织的人员增加呈指数级增长)
微服务的应用架构
整体架构
- 应用架构应该包含整个环境
- 架构师的职责
- 准则——为了实现更高一层的技术目标或者组织目标,团队要遵循的一套指南
- 概念模型——系统内部相互联系以及应用层面的模式的抽象模型
四层架构:平台层、服务层、边界层(接入层)、客户端。
- 平台层:即基础设施,包括容器、LB、日志监控平台、部署流水线、安全解决方案(网络控制、涉密管理、安全加固)、通信信道和服务发现机制
- 服务层:
- 功能分类:业务功能(业务逻辑)和技术能力(公共服务/组件)
- 职责分类:聚合器(aggregator,将底层服务数据关联后输出聚合查询结果)与协调器(coordinator,向下游发出各种指令并编配行动)// 感觉差别不大,只是读、写的区别
- 重要程度分类:关键路径与非关键路径。
- 边界层(接入层):
- 职责:封装抽象底层能力、协议转换、认证鉴权、限流、缓存、日志和指标收集
- 几种不同的边界模式:
- API网关:要避免将业务逻辑渗透到API网关中。
- BFF:根据终端来区分网关,API网关的一种变形
- 消费者驱动网关:GraphQL,可以把它看作BFF方案的一种进化版:不构建多个API,开发者可以只构建一个“超级”API来让消费者决定他们所需要的响应数据的样子。
- 客户端:前端单体、微前端
微服务的事务与查询
分布式事务的一致性可选方案
- 2PC:two phase commit // http://icyfenix.cn/architect-perspective/general-architecture/transaction/distributed.html
- saga编排:回滚可以达到语义上的一致
分布式系统的查询
- 保存数据副本:带来的缓存失效问题。高可用容易,一致性难
- 命令-查询分离:CQRS架构
- 优势:构建专门的查询服务,可以针对性优化,关注点分离
- 问题:复制延迟(replication lag),需要考虑最终一致性
- 可以缓解问题的方案:乐观更新、轮询、发布订阅
设计高可靠服务
可用性定义
服务正常运行时间uptime/(服务正常运行时间uptime+故障时间downtime)。整体可用性不等于某个微服务的可用性
高可靠的通信方案
- 重试retry
- 需要服务支持调用幂等性才允许实施。
- 实现方案:python的tenacity等库支持,不用自己实现
- 防止重试过来的雪崩:
- 指数退避(exponential back-off):给系统一个较低负载的时间,用于恢复
- 指数退避应包括随机元素(jitter):避免涟漪效应(大量重试同时执行)
- 限制次数、重试环境隔离
- *后备(fallback)**方案:服务故障时可选的几种后备、替代方案
- 优雅降级:故障时提供部分基础能力
- 缓存:使用缓存数据(本地、远程)替换
- 功能冗余:针对故障场景提供独立的服务、数据源来替代
- 超时:避免雪崩
- 熔断器(Circuit Breaker):open、hallf-open、close状态。
- 断路器需要发送试验性的请求,以判断连接是否恢复到健康状态。在这个试验状态中,断路器处于半开状态(half open):如果调用成功,断路器就会闭合;否则,继续保持断开状态。
最大限度地提供单个服务的可用性
- 负载均衡
- 限流
- 验证:压力测试和混沌测试
构建可复用的微服务架构
微服务底座的重要性?
微服务部署
自动化
- 自动化的部署方案对于大规模微服务开发而言是必不可少的。
- 比如Google的网站可靠性(site reliability)团队认为大概有70%的服务不可用是由于对生产环境的修改导致的
基于容器和调度器的部署
- 容器并不是使用微服务的必要条件。开发者可以使用许多方法来部署服务,比如使用单虚拟机单服务模型。但是借助调度程序,容器提供了一种特别优雅和灵活的方法来满足我们的两个部署目标:速度和自动化。
- 服务容器化:docker
- 集群部署:kubernetes
简化了对任意数量的独立服务的伸缩扩容、健康检查和发布的管理
构建交付流水线
- 部署流水线是构建持续交付的核心基石
- 理想的微服务部署流程应该满足两大目标。
- 节奏安全——新服务和变更的部署速度越快,开发者迭代开发和向终端用户交付成品的速度也就越快。部署工作应该尽可能提高安全性,开发者应该尽一切可能对给定的变更进行验证,确保其不会对服务的稳定性产生负面影响。
- 一致性——不管底层采用哪种技术栈,各个服务都采用同一套部署流程有助于降低技术隔离,并使操作更具可预测性和可扩展性。
- 工具:Jenkins
可观测(Observability)
可度量(Metrics)
Metric 往往是一些聚合的信息,相比 Logging 丧失了一些具体信息,但是占用的空间要比完整日志小的多,可以用于监控和报警,在这方面 Prometheus 已经基本上成为了事实上的标准
- 监控范围:我们讨论了架构层次:客户端、边界层、服务层和平台层。也应该把这4层都监控起来。
- 监控维度:四大黄金标志:时延、错误量、通信量(即请求量)和饱和度(承载能力)。
- 饱和度:主要强调最能影响服务状态的受限制的资源。 例如,如果系统主要受内存影响,那就主要关注系统的内存状态,如果系统主要受限与磁盘I/O,那就主要观测磁盘I/O的状态。因为通常情况下,当这些资源达到饱和后,服务的性能会明显下降。同时还可以利用饱和度对系统做出预测,比如,“磁盘是否可能在4个小时候就满了”。
- 展示方式:聚焦于最重要的部分,还应当为每个指标设定合适的百分位:99分位、95分位、75分位。
日志(Logging)
Logging 主要记录一些离散的事件,应用往往通过将定义好格式的日志信息输出到文件,然后用日志收集程序收集起来用于分析和聚合。虽然可以用时间将所有日志点事件串联起来,但是却很难展示完整的调用关系路径
- 采用一致的、结构化方式以机器可读的格式存储日志,包含时间戳、唯一seq、来源模块、level等信息。可借用外部工具like logstash
- 日志基础设施:配置完日志聚合功能后,所有服务就开始同时向中心化系统发送度量指标和日志信息了,这能够提高系统的可观测性。
- 开源解决方案:ELK(Elasticsearch+Logstash+Kibana)+ Fluentd(收集器)
分布式链路追踪(Tracing)
Tracing 介于 Logging 和 Metric 之间, 以请求的维度来串联服务间的调用关系并记录调用耗时,即保留了必要的信息,又将分散的日志事件通过 Span 串联,帮助我们更好的理解系统的行为、辅助调试和排查性能问题。
- OpenTracing 旨在标准化 Trace 数据结构和格式,它不是一个标准,OpenTracing API 提供了一个标准的、与供应商无关的框架,是对分布式链路中涉及到的一系列操作的高度抽象集合。
- OpenTracing API: 见相关规范
微服务团队建设
限制团队规模
将工程师划分到不同的独立团队是组织发展壮大后很自然的结果。这对于帮助组织高效扩张是非常必要的。
- 小团队优势:
- 确保沟通渠道保持可控(图13.1展示了沟通关系的增长)。这有助于增强团队的活力和协作,同时缓解冲突。对于团队的“合适规模”存在许多探索,比如JeffBezos的两块披萨规则或Michael Lopp的7+/−3公式。
- 清楚地描述了团队的责任和义务,同时提高了独立性和灵活性。
- 带来的问题:
- 团队之间会出现文化分裂,不同团队遵循和接受的是不同的质量实践或者工程价值观。
- 团队在和其他团队协作时,需要投入额外的精力优先来协调存在竞争关系的事项。
- 团队划分会造成专业知识的隔离,不利于了解全局或者整体效率
- 团队间会存在重复工作,从而导致效率低下
团队模型:两种组织团队的方式
- 按职能划分:如UI团队、后端、前端、测试、PMO等
- 优势:能优化专业知识,有效分享知识和解决方案;职业发展和技能提升更加清晰。
- 劣势:
- 所有权不清晰:没有哪个团队对业务结果或业务价值有明确的所有权,他们只是价值链中的一颗螺钉。
- 缺乏自治:这些团队之间是紧密耦合的,而不是自治的。他们的工作优先级是在其他地方制订的,当工作需要跨多个团队时,团队被阻塞和开发工作被阻碍的可能性就会增加
- 没有长期责任感:面向项目的方式不利于对所开发的代码或产品质量承担起长期责任。
- 竖井风险:目标上出现分歧,不能为对方着想,无法进行有效的协作。
- 跨职能分组:由具有不同专长和角色的人员组成,旨在实现特定的业务目标。我们可以将这些团队称为市场驱动型团队
- 更加有利于微服务团队。优势:能更加快速的交付、沟通渠道短、协作本地化、目标一致。
- 跨职能的团队应该有一个使命,这个使命是鼓舞人心的:它给团队提供了奋斗目标,但也设定了团队职责的边界
- 平台团队:负责基础设施的建设。构建一个微服务平台——部署流程、底座、工具操作和监控系统
- 工程团队要对自己所负责服务产生的告警保持随时待命;平台和基础设施团队要对底层的基础设施或者公享服务(比如部署)保持随时待命;两个团队之间存在的升级途径可以支持对告警进一步调查。
- 知识共享:DRY原则
- 应用了Spotify的分会和协会模式???
实践建议
- 架构师的角色:架构的最佳起点是设定一些原则,原则是团队为了实现更高级别的目标而应该遵循的指导方针(有时是规则)。这些原则会指导团队实践
- 开源模型:将开源原则应用于微服务代码有助于减少争论和缓解技术割裂,同时能够改善知识分享的情况
- 设计评审:对于任何新的服务或重要的新特性,负责这部分工作的工程师都会编写一个设计文档(我们称之为RFC或请求评论),并要求评审员进行反馈,这些评审员来自他们自己团队内部和外部团队
See all postsSee all posts