这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天
微服务架构介绍
架构演变历史
单体架构
优点:① 性能最高 ② 冗余小
劣势:① debug 困难 ② 模块互相影响 ③ 模块分工、开发流程
垂直应用架构
按照业务线垂直划分
优势: 业务独立开发维护
劣势:① 不同业务存在冗余 ② 每个业务还是单体
分布式架构
抽出业务无关的公共模块
优势:业务无关的独立服务
劣势:① 服务模块bug可导致全站瘫痪 ② 调用关系复杂 ③ 不同服务冗余
SOA架构
面向服务
优势:服务注册
劣势:① 整个系统设计是中心化的 ② 需要从上至下 ③ 设计重构困难
微服务架构
彻底的服务化
优势:① 开发效率 ② 业务独立 ③ 设计设计 ④ 自下而上 ⑤ 故障隔离
劣势:① 治理、运维难度 ② 观测挑战 ③
安全性 ④ 分布式系统
微服务架构概览
微服务架构核心要素
微服务架构原理及特征
基本概念
服务 (service)
一组具有相同逻辑的运行实体。
实例 (instance)
一个服务中,每个运行实体即为一个实例
实例与进程的关系
实例与进程之间没有必然对应关系,可以一个实例可以对应一个或多个进程 (反之不常见)
集群 (cluster)
通常指服务内部的逻辑划分,包含多个实例。
常见的实例承载形式
进程、VM、k8s pod
有状态 / 无状态服务
服务的实例是否存储了可持久化的数据(例如磁盘文件)。
服务间通信
对于单体服务,不同模块通信只是简单的函数调用
对于微服务,服务间通信意味着网络传输。
服务注册及发现
问题
在代码层面,如何指定调用一个目标服务的地址 (ip:port) ?
hardcode
// Service A wants to call service B
client := grpcNewClient(“10.23.45.67:8080”
缺点:服务有多个实例,没法 hardcode (记住一个服务的所有实例都是运行同一份代码)服务实例 ip port 本身是动态变化的
DNS
缺点:① 本地 DNS 存在缓存,导致延时 ② 负载均衡问题 ③ 不支持服务实例的探活检查 ④ 域名无法配置端口。
解决
新增一个统一的服务注册中心,用于存储服务名到服务实例的映射
服务实例下线及上线过程
下线:先删除注册中心的实例,再删除服务的实例
上线:先增加服务的实例,进行健康检查,通过后再增加注册中心的实例
流量特征
① 统一网关入口 ② 内网通信多数采用RPC ③ 网状调用链路
核心服务治理功能
服务发布
让一个服务升级运行新的代码的过程
难点:① 服务不可用 ② 服务抖动 ③ 服务回滚
蓝绿部署
蓝绿两个集群,断其中一个,升级另一个,另一个一样,最后两个都升级了
优点:简单稳定
缺点:需要两倍资源
灰度发布
流量治理
在微服务架构下,我们可以基于地区、集群、实例、请求等维度,
对端到端流量的路由路径进行精确控制
负载均衡
常见的 LB 策略:① Round Robin ② Random ③ Ring Hash ④ Least Request ...
稳定性治理
网络攻击,流量突增,机房断电,光纤被挖,机器故障,网络故障,机房空调故障...
典型的稳定性治理
字节跳动服务治理实践
重试的意义
本地函数无重试必要
① 避免掉偶发的错误:提高 SLA (Service-Level Agreement)
② 降低错误率:假设单次请求的错误概率为 0.01,那么连续两次错误概率则为 0.0001。
③ 降低长尾延时:对于偶尔耗时较长的请求,重试请求有机会提前返回
④ 容忍暂时性错误:重试可以尽量规避某些时候系统会有暂时性异常 (例如网络抖动),重试可以避免
⑤ 避开下游故障实例:一个服务中可能会有少量实例故障 (例如机器故障) ,重试其他实例可以成功。
重试的难点
幂等性
多次请求可能会造成数据不一致
重试风暴
随着调用深度的增加,重试次数会指数级上涨
超时设置
假设一个调用正常是 1s 的超时时间,如果允许一次重试,那么第一次请求经过多少时间时,才开始重试呢?
重试策略
限制重试比例
设定一个重试比例阈值 (例如 1%) ,重试次数占所有请求比例不超过该阈值
防止链路重试
链路层面的防重试风暴的核心是限制每层都发生重试,理想情况下只有最下一层发生重试可以返回特殊的 status 表明“请求失败,但别重试”。
Hedged requests
对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应
重试效果验证
实际验证经过上述重试策略后,在链路上发生的重试放大效应