微服务架构 | 青训营笔记

188 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天

微服务架构介绍

系统架构演变历史

单体架构->垂直应用架构->分布式架构->SOA架构->微服务架构

单体架构

全部逻辑运行在一个程序中

优势:

  • 性能好
  • 冗余小

劣势:

  • debug困难
  • 模块互相依赖
  • 开发流程困难

垂直应用架构

按照业务进行分类, 垂直切分

优势:

  • 业务独立开发

劣势:

  • 不同业务存在冗余
  • 每个业务仍然是单体结构

分布式架构

抽出与业务无关的公共模块

image-20230204123456526.png

优势:

  • 业务无关的独立服务

劣势:

  • 服务模块bug可导致全站瘫痪
  • 调用关系复杂
  • 不同服务冗余

SOA架构

服务导向, 面向服务, 有一个统一的服务注册中心

优势:

  • 服务注册, 降低业务逻辑与后台服务的耦合

劣势:

  • 整个系统设计是中心化的
  • 需要从上至下设计
  • 重构困难

微服务架构

彻底的服务化

13b828a6-67d4-4db2-a1fe-5551f830466a.png

优势:

  • 开发效率
  • 业务独立设计
  • 自下而上
  • 故障隔离

劣势:

  • 治理, 运维难度
  • 如何观测服务运行细节
  • 安全性
  • 分布式系统的难题

微服务架构概览

f98445c1-7572-4174-bd7e-5eb97eaa6e58.png

除了业务部分的模块外, 还需要拥有服务配置和治理的部分用于协调各服务之间的交互, 还要有链路追踪的部分用于监控每个服务的运行状态

微服务架构核心要素

  • 服务治理: 服务注册, 服务发现, 负载均衡, 扩缩容, 流量治理, 稳定性治理
  • 可观测性: 日志采集, 日志分析, 监控, 异常报警, 链路追踪
  • 安全: 身份验证, 黑产攻击等

微服务架构原理及特征

基本概念

  • 服务service: 一组具有相同逻辑的运行实体
  • 实例instance: 一个服务中, 每个运行实体就是一个实例
  • 集群cluster: 服务内部的逻辑划分, 包含多个实例
  • 实例承载形式: 进程, VM, k8s pod
  • 有状态/无状态服务: 服务的实例是否存储了可持久化的数据

7b0577cc-02e1-4d6c-934c-6767e72b303a.png

  • 服务间通信: 对于微服务而言, 服务间通信意味着网络传输(http, grpc, thrift)

服务注册及发现

问题: 在代码层面, 如何指定一个服务调用的目标地址(ip:port)

使用确定的地址和端口, 会导致服务只调用某一个实例, 不灵活

新增一个统一的服务注册中心, 用于存储服务名到服务实例的映射

  • 服务上下线过程: 先在注册中心去除对应实例的映射, 等到当前流量全部处理后, 再下线对应的实例

流量特征

  • 统一网关入口, 分流外部http请求
  • 内网通信采用rpc
  • 网状调用链路

核心服务治理功能

服务发布(deployment)

让一个服务升级运行新的代码的过程

难点:

  • 服务不可用: 升级时服务不能处理外界请求
  • 服务抖动: 某些实例升级中不可用, 导致服务的运行结果不稳定
  • 服务回滚: 如果新的代码出现问题, 需要进行服务的回滚

蓝绿部署

将服务分为两个集群, 每次下线其中一个, 将流量引到可用的另一个集群, 等到部署完成再反过来升级另一个集群

简单, 稳定, 但是部署过程中其中一个集群需要承担两倍的流量

灰度发布(金丝雀发布)

image-20230204135528081.png

先上线一个实例, 试探服务可行性, 再逐步更新其它实例

问题: 对流量控制的精细要求比较高, 并且当服务出现问题需要回滚时比较复杂

流量治理

基于地区, 集群, 实例, 请求等维度对端到端的流量路由路径进行控制

8799dc63-1460-4db9-a305-c34afbe17648.png

  • 根据地区服务器的承载能力划分流量
  • 将少部分流量分流到较新但是功能不稳定的集群
  • 根据机器性能划分流量
  • 对于请求可以打上标签标记是否是用于测试的请求, 方便将用于测试的流量分流到测试集群上, 同时也可以走一遍同样的请求流程

负载均衡

Load Balance, 分配请求在每个下游实例上的分布

策略:

  • round robin
  • random
  • ring hash
  • least request

稳定性治理

image-20230204140553686.png

  • 限流: 限制请求数量
  • 熔断: 当连接失败时直接拒绝请求, 但周期性试探确认服务是否重新可用
  • 过载保护: 识别负载情况, 进行过载保护
  • 降级: 当负载过高, 拒绝优先级低的服务请求

服务治理实践——重试

重试的意义

在微服务架构中, 函数调用异常可能来源于不可预期的网络因素或物理因素

重试可以避免这类偶发的错误, 提高SLA(service-level agreement)

  • 降低错误率: 对于错误率为0.01的一次请求, 两次请求可以将错误率降为0.0001
  • 降低长尾延时: 对于偶尔耗时比较长的请求, 重试有机会提前返回
  • 容忍暂时性错误
  • 避开下游故障实例: 当某个实例不可用, 通过重试选择其它实例

重试的难点

  • 幂等性
  • 重试风暴: 对于一条规模比较大的服务调用链, 如果最下游某个服务不可用, 将会导致上游的所有服务都对自己上游服务的所有请求进行重试, 导致指数级增长的请求数
  • 超时设置: 如何设置一个合适的超时时间. 不同环境下, 超时时间可能不同

重试策略

  • 限制重试比例: 设定一个重试比例的阈值, 重试次数占所有请求比例不超过阈值
  • 防止链路重试: 当某个下游服务出现问题, 只有这个服务的上游服务进行重试, 更上游的服务接收到特殊的错误码(请求失败, 但不重试)后不重试
  • hedged request: 对于可能超时的请求, 重新向下游发送一个相同的请求, 如果这个请求更早返回, 则降低了延迟