微服务实战读书笔记

January 10, 2022

微服务的定义

三大关键特性

  • 每个微服务只负责一个功能
  • 每个微服务都有自己的数据存储 // 低耦合,只能通过接口访问对方数据
  • 微服务自己负责编排和协作 // 高内聚?

两个基本特性

  • 可独立部署 // 非庞大的单体应用
  • 可替代

微服务架构原则

  • 自治性:松耦合,可独立部署
  • 可恢复性:故障隔离,模块故障仅影响微服务自身。 // 是不是叫“隔离”更加合适
  • 透明性:每个微服务有完整的日志、监控以及基础设施数据
  • 自动化:保障系统频繁部署和运维的正确性,通过引入流水线or云函数
  • 一致性:微服务需要考虑向后兼容,确保不需要强迫其他团队升级或者破坏服务之间的复杂交互。

为何选择微服务

  • 技术差异性:允许根据服务的特性选择不同的技术栈
  • 系统复杂度会逐步增加:不断缩短的交付周期
  • 降低重复和风险:灵活性、部署风险

微服务的设计、运维挑战

  • 划定微服务范围需要领域知识:如何划分某个服务的职责需要对领域有充分的理解
  • 良好的契约设计 // 多团队设计,需要服务契约保障完整、简洁及可预测(协议定义反映了真实表现
  • 分布式带来的风险:网络、带宽
  • 故障点增多:微服务并不能消除风险,而是将这个成本移到了系统生命周期的后半阶段:降低了开发过程中的冲突,但是增加了运维阶段系统部署、验证以及监控的复杂度。
  • 运维挑战:难以实现的可观测性、多故障同时出现(需要考虑服务的鲁棒性)

微服务的开发生命周期

  • 设计
  • 部署
    • 复杂的部署过程就是“通下水道”(plumbing)【用来比喻一些价值不大的脏活累活,而不能为客户创造任何价值】。
    • 部署需要容器化、流水线化,尽可能的“单调
  • 监控
    • 发现潜在的薄弱环节并进行重构
    • 重点指标:业务指标、应用日志、运维指标和基础设施指标
  • 其他:有责任感和运维意识的工程师文化
    康威定律:组织沟通方式会通过系统设计表达出来(沟通成本随着项目或者组织的人员增加呈指数级增长)

微服务的应用架构

整体架构

  • 应用架构应该包含整个环境
  • 架构师的职责
    • 准则——为了实现更高一层的技术目标或者组织目标,团队要遵循的一套指南
    • 概念模型——系统内部相互联系以及应用层面的模式的抽象模型

四层架构:平台层、服务层、边界层(接入层)、客户端。

  • 平台层:即基础设施,包括容器、LB、日志监控平台、部署流水线、安全解决方案(网络控制、涉密管理、安全加固)、通信信道和服务发现机制
  • 服务层:
    • 功能分类:业务功能(业务逻辑)和技术能力(公共服务/组件)
    • 职责分类:聚合器(aggregator,将底层服务数据关联后输出聚合查询结果)与协调器(coordinator,向下游发出各种指令并编配行动)// 感觉差别不大,只是读、写的区别
    • 重要程度分类:关键路径与非关键路径。
  • 边界层(接入层)
    • 职责:封装抽象底层能力、协议转换、认证鉴权、限流、缓存、日志和指标收集
    • 几种不同的边界模式:
      • API网关:要避免将业务逻辑渗透到API网关中。
      • BFF:根据终端来区分网关,API网关的一种变形
      • 消费者驱动网关:GraphQL,可以把它看作BFF方案的一种进化版:不构建多个API,开发者可以只构建一个“超级”API来让消费者决定他们所需要的响应数据的样子。
  • 客户端:前端单体、微前端

微服务的事务与查询

分布式事务的一致性可选方案

分布式系统的查询

  • 保存数据副本:带来的缓存失效问题。高可用容易,一致性难
  • 命令-查询分离: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