这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
微服务架构介绍
系统架构演变历史
单体架构->垂直应用架构->分布式架构->SOA架构->微服务架构
单体架构
全部逻辑运行在一个程序中
优势:
- 性能好
- 冗余小
劣势:
- debug困难
- 模块互相依赖
- 开发流程困难
垂直应用架构
按照业务进行分类, 垂直切分
优势:
- 业务独立开发
劣势:
- 不同业务存在冗余
- 每个业务仍然是单体结构
分布式架构
抽出与业务无关的公共模块
优势:
- 业务无关的独立服务
劣势:
- 服务模块bug可导致全站瘫痪
- 调用关系复杂
- 不同服务冗余
SOA架构
服务导向, 面向服务, 有一个统一的服务注册中心
优势:
- 服务注册, 降低业务逻辑与后台服务的耦合
劣势:
- 整个系统设计是中心化的
- 需要从上至下设计
- 重构困难
微服务架构
彻底的服务化
优势:
- 开发效率
- 业务独立设计
- 自下而上
- 故障隔离
劣势:
- 治理, 运维难度
- 如何观测服务运行细节
- 安全性
- 分布式系统的难题
微服务架构概览
除了业务部分的模块外, 还需要拥有服务配置和治理的部分用于协调各服务之间的交互, 还要有链路追踪的部分用于监控每个服务的运行状态
微服务架构核心要素
- 服务治理: 服务注册, 服务发现, 负载均衡, 扩缩容, 流量治理, 稳定性治理
- 可观测性: 日志采集, 日志分析, 监控, 异常报警, 链路追踪
- 安全: 身份验证, 黑产攻击等
微服务架构原理及特征
基本概念
- 服务service: 一组具有相同逻辑的运行实体
- 实例instance: 一个服务中, 每个运行实体就是一个实例
- 集群cluster: 服务内部的逻辑划分, 包含多个实例
- 实例承载形式: 进程, VM, k8s pod
- 有状态/无状态服务: 服务的实例是否存储了可持久化的数据
- 服务间通信: 对于微服务而言, 服务间通信意味着网络传输(http, grpc, thrift)
服务注册及发现
问题: 在代码层面, 如何指定一个服务调用的目标地址(ip:port)
使用确定的地址和端口, 会导致服务只调用某一个实例, 不灵活
新增一个统一的服务注册中心, 用于存储服务名到服务实例的映射
- 服务上下线过程: 先在注册中心去除对应实例的映射, 等到当前流量全部处理后, 再下线对应的实例
流量特征
- 统一网关入口, 分流外部http请求
- 内网通信采用rpc
- 网状调用链路
核心服务治理功能
服务发布(deployment)
让一个服务升级运行新的代码的过程
难点:
- 服务不可用: 升级时服务不能处理外界请求
- 服务抖动: 某些实例升级中不可用, 导致服务的运行结果不稳定
- 服务回滚: 如果新的代码出现问题, 需要进行服务的回滚
蓝绿部署
将服务分为两个集群, 每次下线其中一个, 将流量引到可用的另一个集群, 等到部署完成再反过来升级另一个集群
简单, 稳定, 但是部署过程中其中一个集群需要承担两倍的流量
灰度发布(金丝雀发布)
先上线一个实例, 试探服务可行性, 再逐步更新其它实例
问题: 对流量控制的精细要求比较高, 并且当服务出现问题需要回滚时比较复杂
流量治理
基于地区, 集群, 实例, 请求等维度对端到端的流量路由路径进行控制
- 根据地区服务器的承载能力划分流量
- 将少部分流量分流到较新但是功能不稳定的集群
- 根据机器性能划分流量
- 对于请求可以打上标签标记是否是用于测试的请求, 方便将用于测试的流量分流到测试集群上, 同时也可以走一遍同样的请求流程
负载均衡
Load Balance, 分配请求在每个下游实例上的分布
策略:
- round robin
- random
- ring hash
- least request
稳定性治理
- 限流: 限制请求数量
- 熔断: 当连接失败时直接拒绝请求, 但周期性试探确认服务是否重新可用
- 过载保护: 识别负载情况, 进行过载保护
- 降级: 当负载过高, 拒绝优先级低的服务请求
服务治理实践——重试
重试的意义
在微服务架构中, 函数调用异常可能来源于不可预期的网络因素或物理因素
重试可以避免这类偶发的错误, 提高SLA(service-level agreement)
- 降低错误率: 对于错误率为0.01的一次请求, 两次请求可以将错误率降为0.0001
- 降低长尾延时: 对于偶尔耗时比较长的请求, 重试有机会提前返回
- 容忍暂时性错误
- 避开下游故障实例: 当某个实例不可用, 通过重试选择其它实例
重试的难点
- 幂等性
- 重试风暴: 对于一条规模比较大的服务调用链, 如果最下游某个服务不可用, 将会导致上游的所有服务都对自己上游服务的所有请求进行重试, 导致指数级增长的请求数
- 超时设置: 如何设置一个合适的超时时间. 不同环境下, 超时时间可能不同
重试策略
- 限制重试比例: 设定一个重试比例的阈值, 重试次数占所有请求比例不超过阈值
- 防止链路重试: 当某个下游服务出现问题, 只有这个服务的上游服务进行重试, 更上游的服务接收到特殊的错误码(请求失败, 但不重试)后不重试
- hedged request: 对于可能超时的请求, 重新向下游发送一个相同的请求, 如果这个请求更早返回, 则降低了延迟