这是我参与「第五届青训营 」伴学笔记创作活动的第10天
重点概览
-
微服务架构介绍
- 微服务架构的背景由来、架构概览、基本要素
-
微服务架构原理及特征
- 微服务架构的基本组件、工作原理、流量特征
-
核心服务治理功能
- 核心的服务治理功能,包括流量治理、服务均衡、稳定性治理
-
字节跳动服务治理实践
- 字节跳动在微服务架构稳定性治理中,对请求重试策略的探索及实践
详细介绍
参考文章:
系统架构演进背景:
互联网爆炸性发展
硬件设施的快速发展
需求复杂性多样化
开发人员急剧增加
计算机理论及技术发展
系统架构演变历史
↓ 单体架构 优势: 1 性能最高 2 冗余小 劣势: 1 debug困难 2 模块互相影响 3 开发人员多时, 模块分工, 开发流程复杂 ↓ 垂直应用架构 优势: 1 业务独立开发维护 劣势: 1 不同业务存在冗余 2 每个业务还是单体 ↓ 分布式架构 抽出业务无关的公共模块 优势: 1 业务无关的独立服务 劣势: 1 服务模块bug可导致全站瘫痪 2 调用关系复杂 3 不同服务冗余 ↓ SOA架构 Service Oriented Architecture: 面向服务 优势: 1 服务注册 劣势: 1 整个系统设计是中心化的 2 需要从上至下设计 3 重构困难 ↓ 微服务架构 彻底的服务化 优势: 1 开发效率 2 业务独立设计 3 自下而上 4 故障隔离 劣势: 1 治理, 运维难度 2 观测挑战 3 安全性 4 分布式系统的劣势
微服务架构核心要素
- 服务治理 服务注册 服务发现 负载均衡 扩缩容 流量治理 稳定性治理 ...
- 可观测性 日志采集 日志分析 监控打点 监控大盘 异常报警 链路追踪 ...
- 安全 身份验证 认证授权 访问令牌 审计 传输加密 黑产攻击 ...
微服务架构原理及特征
基本概念
服务(service): 一组具有相同逻辑的运行实体
实例(instance): 一个服务中, 每个运行实体即为一个实例
实例与进程的关系 实例与进程之间没有必然对应关系, 可以一个实例可以对应一个或多个进程(反之不常见)
集群(cluster): 通常指服务内部的逻辑划分, 包含多个实例 常见的实例承载形式:进程, VM, k8s pod ...
有状态/无状态服务: 服务的实例是否存储了可持久化的数据(例如磁盘文件)
服务间通信: 对于单体服务, 不同模块通信只是简单的函数调用 对于微服务, 服务间通信意味着网络传输
服务注册及发现
问题: 在代码层面, 如何指定调用一个目标服务的地址(ip:port)?
hardcode? 如, client := grpc.NewClient("10.23.45.67:8080") 不行的原因:
- 地址是动态的, 不能写死
- 地址不止一个
DNS? 不行的原因: 1 本地DNS存在缓存, 导致延时 2 负载均衡问题 3 不支持服务实例的探活检查 4 域名无法配置端口
解决思路: 新增一个统一的服务注册中心, 用于存储服务名到服务实例的映射
服务实例上线及下线过程 下线前, 会先在服务中心注销, 等到没有流量打过来了才会下线 上线前, 会进行health check
流量特征
统一网关入口 内网通信多数采用RPC 网状调用链路
核心服务治理功能
服务发布
服务发布(deployment): 让一个服务升级运行新代码的过程 难点: 1 服务不可用 2 服务抖动 3 服务回滚
蓝绿部署 优点: 简单, 稳定 缺点: 需要两倍资源 适合在流量低谷进行 灰度发布(金丝雀发布) 优点: 比蓝绿部署省资源 缺点: 1 服务回滚比蓝绿部署难很多
流量治理
在微服务架构下, 我们可以基于地区, 集群, 实例, 请求等维度, 对端到端流量的路由路径进行精确控制
负载均衡
负载均衡(Load Balance): 负载分配请求在每个下游实例上的分布 常见的LB策略 Round Robin Random Ring Hash Least Request ...
稳定性治理
线上服务总是会出问题的, 这与程序的正确性无关 原因: 网络攻击 流量突增 机房断电 光纤被挖 机器故障 网络故障 机房空调故障 ...
微服务架构中典型的稳定性治理功能:
- 限流
- 熔断
- 过载保护
- 降级 (识别流量等级, 拒绝低级流量, 处理重要流量)
字节跳动服务治理实践
-
请求重试的意义
-
本地函数调用
- 通常没有重试意义
-
远程函数调用
- 网络抖动、下游负载高、下游机器宕机......
- 重试是有意义的,可以避免偶发性的错误,提高 SLA
-
重试的意义
- 降低错误率
- 降低长尾延时
- 容忍暂时性错误
- 避开下游故障实例
-
-
请求重试的难点
-
幂等性
- POST 请求可以重试吗?
-
重试风暴
- 随着调用链路的增加,重试次数呈指数级上升
-
超时设置
- 假设调用时间一共1s,经过多少时间开始重试?
-
-
重试策略
-
限制重试比例
- 设定一个重试比例阈值(例如 1%),重试次数占所有请求比例不超过该阈值
-
防止链路重试
- 返回特殊的 status code,表示“请求失败,但别重试”
-
Hedged Requests
- 对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应
-
-
重试效果验证
- 字节跳动重试组件能够极大限制重试发生的链路放大效应