这是我参与「第五届青训营 」笔记创作活动的第14天
一、课程背景
- 为什么有这门课?
微服务架构是当前大多数互联网公司的标准架构,与开源生态契合,大量针对微服务架构的开源项目,当前主流的系统架构。
- 我们可以学到什么?
微服务架构的由来以及原理;
服务治理功能是如何工作的。
二、本堂课重点知识
- 微服务架构介绍
- 微服务架构原理及特征
- 核心服务治理功能
- 字节跳动服务治理实践
三、详细知识点介绍
1. 微服务架构介绍
1.1 系统架构演变历史
为什么系统架构需要演进?
- 互联网的爆炸性发展
- 硬件设施的快速发展:包括 CPU MEM 存储 网络
- 需求复杂性的多样化:包括 文本 图片 音频 视频 VR
- 开发人员的急剧增加:早期的精英程序员 如今的易于上手的开发平台
- 计算机理论及技术的发展:算法 (Paxos Raft) NoSQL 大数据
1.1.1 单体架构
all in one process
优势:
- 性能最高
- 冗余小
劣势:
- debug 困难
- 字节有上万研发,试想全部开发成一个程序,debug 会是什么体验?
- 模块相互影响
- 非核心功能可能导致程序崩溃
- 模块分工、开发流程
- 几乎无法分工(除了google)
1.1.2 垂直应用架构
按照业务线垂直划分
优势:
- 业务独立开发维护
劣势:
- 不同业务存在冗余
- 无法复用
- 每个业务还是单体
1.1.3 分布式架构
抽出业务无关的公共模块
优势:
- 业务无关的服务独立
劣势:
- 服务模块bug可能导致全站瘫痪
- 一个模块服务有问题,可以导致整个系统崩溃
- 调用关系错综复杂
- 不同服务之间存在冗余
1.1.4 SOA架构(Service Oriented Architecture)
面向服务(开始引入“服务” “服务注册” 的概念)
优势:
- 服务注册
劣势:
- 整个系统设计是中心化的
- 需要从上至下去设计划分
- 重构困难
1.1.5 微服务架构
彻底的服务化(从下而上设计)
优势:
- 开发效率
- 业务独立设计
- 自下而上
- 故障隔离
劣势:
- 治理、运维难度急剧增加
- 观测挑战
- 安全性问题
- 分布式系统本身的复杂性
1.2 微服务架构概览
从组件的角度看看微服务架构的整体视角
1.3 微服务架构的核心要素
- 服务治理
- 服务注册
- 服务发现
- 负载均衡
- 扩缩容
- 流量治理
- 稳定性治理
- ...
- 可观测性
- 日志采集
- 日志分析
- 监控打点
- 监控大盘
- 异常报警
- 链路追踪
- ...
- 安全
- 身份验证
- 认证授权
- 访问令牌
- 审计
- 传输加密
- 黑产攻击
- ...
微服务拆分后带来的挑战:
- 日志采集
- 监控打点
- 链路追踪
2. 微服务架构原理及特征
2.1 基本概念
- 服务(service)
- 一组具有相同逻辑的运行实体(实例与进程之间的关系)
- 实例(instance)
- 一个服务中,每个运行实体即为一个实例
- 实例与进程的关系
- 实例与进程之间没有必然的对应关系,可以一个实例可以对应一个或多个进程(反之不常见)
- 集群(cluster)
- 通常指服务内部的逻辑划分,包含多个实例
- 常见的实例承载形式
- 进程、VM、k8s pod ....
- 有状态/无状态服务
- 服务的实例是否存储了可持久化的数据(例如磁盘文件)
如果把HDFS看做一组微服务:
服务间通信:
对于单体服务,不同模块通信知识简单的函数调用
对于微服务,服务间通信意味着网络传输。
2.2 服务注册及发现
问题:
在代码层面,如何指定调用一个目标服务的地址(ip:port)?
- hardcode?
//Service A wants to call service B
client := grpc.NewClient("10.23.45.67:8080")
hardcode 的方式指定下游实例地址有什么问题? 服务器有多个实例,没法hardcode(记住一个服务器所有实例都是运行同一份代码,服务实例 ip port 本身是动态变化的)
- DNS?
- 本地 DNS 存在缓存,导致延时
- 负载均衡问题
- 不支持服务实例的探活检查
- 域名无法配置端口
思想:中间层
- 解决思路
新增一个统一的服务注册中心,用于存储服务名到服务实例的映射。(与DNS类似,需要引入一个中间层)
服务实例上线及下线过程
了解服务发现的基本机制,我们来看看如何基于服务发现来实现平滑无损的服务实例上下流程
假设系统管理员需要下线 service B 的实例-3,不能直接下线,还有流量
2.3 流量特征
- 统一网关入口
- 内网通信多数采用RPC
- 网状调用链路
弱化连接的概念,强调“请求”,即同一个客户端长连接发出的请求,理论上可以达到服务中所有实例,API gateway 可以作用身份认证,进而将 token 附在请求上
3. 核心服务治理功能
3.1 服务发布
服务发布(deployment),即指让一个服务升级运行新的代码过程
服务发布的难点:
3.1.1 蓝绿部署
简单,稳定,但需要两倍资源
3.1.2 灰度发布(金丝雀发布)
金丝雀(canary)对瓦斯及其敏感,17世纪时,英国矿工在下井前会放一只金丝雀,以确保矿井中没有瓦斯。
在微服务架构下,我们可以基于地区、集群、实例、请求等维度,对端到端的路由路径进行精确控制。
这里指狭义的对流量进行控制
3.3 负载均衡
负载均衡(Load Balance)负责分配请求在每个下游实例上的分布。
常见的LB策略
- Round Robin
- Random
- Ring Hash
- Least Request
- ...
最基本的问题,一个服务中,通常每个实例的负载应当是大体均衡一致的。
3.4 稳定性治理
线上服务总是会出现问题的,这与程序的正确性无关。
- 网络攻击
- 流量突增
- 机房断电
- 光纤被挖
- 机器故障
- 网络故障
- 机房空调故障
- ...
微服务架构中典型的稳定性治理功能:
- 限流
- 熔断
- 过载保护
- 降级
4. 字节跳动服务治理实践
4.1 重试的意义
本地函数调用:
可能有哪些异常?
- 参数非法
- OOM(Out Of Memory)
- NPE(Null Pointer Exception)
- 边界 case
- 系统崩溃
- 死循环
- 程序异常退出
是否有重试的必要?本地函数调用基本没有重试的意义。
远程函数调用:
可能有哪些异常?
- 网络抖动
- 下游负载高导致超时
- 下游机器宕机
- 本地机器负载高,调度超时
- 下游熔断、限流
重试可以避免掉偶发的错误,提高 SLA(Service-Level Agreement)
- 降低错误率
- 假设单次请求的错误概率为 0.01,那么连续两次错误概率则为 0.0001
- 降低长尾延时
- 对于偶尔耗时较长的请求,重试请求有机会提前返回
- 容忍暂时性错误
- 某些时候系统会有暂时性异常(例如网络抖动),重试可以尽量规避
- 避开下游故障实例
- 一个服务中可能会有少量实例故障(例如机器故障),重试其他实例可以成功
既然重试这么多好处,为什么默认不用呢?
4.2 重试的难点
- 幂等性
- 多次请求可能会造成数据不一致
- 重试风暴
- 随着调用深度的增加,重试次数会指数级上涨
- 超时设置
- 假设一个调用正常是1s的超时实践,如果允许一次重试,那么第一次请求经过多少时间时,才开始重试呢?
4.3 重试策略
4.3.1 限制重试比
假设一个重试比例阈值(例如1%),重试次数占所有请求比例不超过该阈值。
重试只有在大部分请求都成功,只有少量请求失败时,才有必要
如果大部分请求都失败,重试只会加剧问题严重性
因此,可以定义,比如重试次数不能超过正常成功请求次数的1%
4.3.2 防止链路重试
链路层面的防重试风暴的核心是限制每层都发生重试,理想情况下只有最下一层发生重试。
可以返回特殊的status表明“请求失败,但别重试”。
缺点:对业务代码有侵入性。
4.3.3 Hedged requests
对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应。
4.4 重试效果验证
实验验证经过上述重试策略后,在链路上发生的重试放大效应。
四、课程总结
本堂课了解到微服务的架构思想,让我受益很多,微服务架构区别于传统的单体软件架构,是一种为了适应当前互联网后台服务的「三高需求:高并发、高性能、高可用」而产生的的软件架构,所以,今后逐渐转向微服务架构可以让我学会更多知识。