讲师介绍
周余发:2018年硕士毕业,曾在美团点评研发效率中心、抖音服务架构担任系统开发。目前为字节跳动Service Mesh研发人员。
相关课程
- 《架构初探-谁动了我的蛋糕》
- 《HTTP 框架修炼之道》
- 《RPC 框架设计与实现》
- 《从需求到上线全流程》
课程背景
- 为什么有这门课程? 微服务架构是当前大多数互联网公司的标准架构。
- 我可以学到什么? 微服务架构的由来及原理; 服务治理功能是如何工作的。
1. 微服务架构介绍
微服务架构的背景由来、架构概览、基本要素
1.1 系统架构演变历史
- 互联网的爆炸性发展
- 硬件设施的快速发展
- 需求复杂性的多样化
- 开发人员的急剧增加
- 计算机理论及技术的发展
架构演进过程:单体架构->垂直应用架构->分布式架构->SOA架构->微服务架构
1.1.1 单体架构
all in one process
优势:
- 性能最高
- 冗余小
劣势:
- debug困难
- 模块相互影响
- 模块分功、并发流程
1.png
1.1.2 垂直应用架构
按照业务线垂直划分
优势:
- 业务独立开发维护
劣势:
- 不同业务存在冗余
- 每个业务还是单体
2.png
1.1.3 分布式架构
抽出业务无关的公共模块
优势:
- 业务无关的独立服务
劣势:
- 服务模块bug可导致全站瘫痪
- 调用关系复杂
- 不同服务冗余
3.png
1.1.4 SOA架构(Service Oriented Architecture)
面向服务
优势:
- 服务注册
劣势:
- 整个系统设计是中心化的
- 需要从上至下设计
- 重构困难
4.png
1.1.5 微服务架构
彻底的服务化
优势:
- 开发效率
- 业务独立设计
- 自下而上
- 故障隔离
劣势:
- 治理、运维难度
- 观测挑战
- 安全性
- 分布式系统
5.png
1.2 微服务架构概览
6.png
1.3 微服务架构核心要素
服务治理
服务注册、服务发现、负载均衡、扩缩容、流量治理、稳定性治理 .......
可观测性
日志采集、日志分析、监控打点、监控大盘、异常报警、链路追踪 ......
安全
身份验证、认证授权、访问令牌、审计、传输加密、黑产攻击 ......
2. 微服务架构原理及特征
微服务架构的基本组件、工作原理、流量特征
2.1 基本概念
- 服务(service)
- 一组具有相同逻辑的运行实体。
- 实例(instance)
- 一个服务中,每个运行实体即为一个实例。
- 实例与进程的关系
- 实例与进程之间没有必然对应关系,可以一个实例可以对应一个或多个进程(反之不常见)。
- 集群(cluster)
- 通常指服务内部的逻辑划分,包含多个实例。
- 常见的实例承载形式
- 进程、VM、k8s pod ......
- 有状态/无状态服务
- 服务的实例是否存储了可持久化的数据(列如磁盘文件)。
7.png
如果把HDFS看做一组微服务
8.png
服务间通信
对于单体服务,不同模块通信只是简单的函数调用。
对于微服务,服务间通信意味着网络传输。
9.png
2.2 服务注册及发现
问题1
在代码层面,如何指定调用一个目标服务的地址(ip:port)?
hardcode?
代码语言:javascript复制// Service A wants to call service B.
client := grpc.NewClient("10.23.45.67:8080")
10.png
问题2
在代码层面,如何指定调用一个目标服务的地址(ip:port)?
DNS?
- 本地DNS存在缓存,导致延时。
- 负载均衡问题。
- 不支持服务实例的探活检查。
- 域名无法配置端口。
11.png
解决一个ip指定上万端口问题
解决思路:新增一个统一的服务注册中心,用于存储服务名到服务实例的映射。
12.png
无损的服务实例上下线过程
- 下线操作
如果需要下线实例3(10.67.89.12:9007),首先删除服务中心(Service Registry)内的10.67.89.12:9007,等过一段时间服务A调用不到实例3了,这个时候再删除实例3是安全的(因为已经没有流量了),最终完成下线。
- 上线操作
上线操作与下线完全相反,首先启动一个服务实例4,并且需要一个切换检查(health check)(发送一个请求尝试一下是否成功),之后再讲实例4的ip:端口 注册进入服务中心(Service Registry),注册成功后等待一段时间,服务进行刷新,服务A就会自动连接到实例4上,流量就来了。
14.png
2.3 流量特征
- 统一网关入口
- 内网通信多数采用RPC
- 网络调用链路
15.png
3. 核心服务治理功能
核心的服务治理功能,包括流量治理、服务均衡、稳定性治理
服务发布(deployment),即指让一个服务升级运行新的代码的过程。
3.1 服务发布
服务发布的难点
服务不可用
服务抖动
服务回滚
蓝绿部署
19.png
首先发布绿色的服务,就先将绿色的流量切到蓝色的上面,这个时候绿色没有流量就是安全的不会出现安全问题,之后对绿色进行升级,升级完之后和刚刚步骤一样,先将蓝色的服务切到绿色的上面, 升级蓝色的,全部完成升级后,这个实例就完成了。这样就可以达到无损升级。
优点:简单,稳定
缺点:需要两倍资源(但是可以从流量低峰入手,会降低资源消耗)
灰度发布(金丝雀发布)
金丝雀背景:金丝雀(canary) 对瓦斯极其敏感,17世纪时,英国矿工在下井前会先放入一只金丝雀,以确保矿井中没有瓦斯。
20.png
21.png
整个流程就是:先放入一个金丝雀的服务,启动如果正常没有问题,将之前的一个服务进行替换掉,之后重复操作,直到将旧的全部替换完成。
灰度发布难点:流量切换很麻烦有很大挑战,并且如果出现一个问题例如完成了百分之九十九就要进行服务回滚,将之前的完成部分全部回退回去,所以还需要有蓝绿部署。这也就是节省了资源所需要的代价。
3.2 流量治理
在微服务架构下,我们可以基于地区、集群、实例、请求等维度,对端到端流量的路由路径进行精确控制。
22.png
3.3 负载均衡
负载均衡(Load Balance)负责分配请求在每个下游实例上的分布。
常见的LB策略
- Round Robin
- Random
- Ring Hash
- Least Request
- ......
23.png
3.4 稳定性治理
线上服务总是会出现问题的,这与程序的正确性无关。
- 网络攻击
- 流量突增
- 机房断电
- 光纤被挖
- 机器故障
- 网络故障
- 机房空调故障
- ......
24.png
为了避免上述问题给出了微服务架构中典型的稳定性治理功能
- 限流
25.png
- 熔断
26.png
- 过载保护
27.png
- 降级
28.png
3.5 小结
- 服务发布:蓝绿部署、灰度发布
- 基于地区、集群、实例、请求等维度的流量治理功能。
- 几种常见的负载均衡策略
- 微服务架构中的稳定性治理功能
4. 字节跳动服务治理实践
- 字节跳动在微服务架构稳定性治理中,对请求重试策略的探索及实践
- 重试的意义、重试的难点、重试策略、重试效果验证
4.1 重试的意义
代码语言:javascript复制func LocalFunc(x int) int {
res := calculate(x * 2)
return res
}
可能有哪些异常?
- 参数非法
- OOM(Out Of Memory)
- NPE(Null Pointer Exception)
- 边界case
- 系统崩溃
- 死循环
- 程序异常退出
是否有重试必要?没必要
代码语言:javascript复制func RemoteFunc(ctx context.Context, x int) (int, error) {
ctx2, defer_func := context.WithTimeout(ctx, time.Second)
defer defer_func()
res, err := grpc_client.Calculate(ctx2, x * 2)
return res, err
}
可能有哪些异常?
- 网络抖动
- 下游负载高导致超时
- 下游机器宕机
- 本地机器负载高,调度超时
- 下游熔断、限流
- ......
重试可以避免掉偶发的错误,提高SLA(Service-Level Agreement)
代码语言:javascript复制func RmotFunc(ctx context.Context, x int) (int , error) {
ctx2, defer_func := context.WithTimeout(ctx, time.Second)
defer defer_func()
res, err := grpc_client.Calculate(ctx2, x * 2)
return res, err
}
func RemoteFuncRetry(ctx context.Context, x int) (res int, err error) {
for i := 0; i < 3; i {
if res,err = RemoteFunc(ctx, x); err == nil {
return
}
}
return
}
重试的意义
- 降低错误率
- 假设单词请求的错误概率为0.01,那么连续两次错误概率则为0.0001。
- 降低长尾延时
- 对于偶尔耗时较长的请求,重试请求有机会提前返回。
- 容忍暂时性错误
- 某些时候系统会有暂时性异常(例如网络抖动),重试可以尽量规避。
- 避开下游故障实例
- 一个服务中可能会有少量实例故障(例如机器故障),重试其他实例可以成功。
4.2 重试的难点
既然重试这么多好处,为什么默认不用呢?
有以下几个难点
- 幂等性
- 重试风暴
- 超时设置
重试风暴(很容易出现雪崩)
雪崩和调用链有关系
29.png
上面这个图片清楚的描述出了由服务A重试3词,到服务B加上之前的就需要9次是一个幂的请求次数,最终达到一定量就会出现雪崩
那如何解决呢?
4.3 重试策略
设定一个重试比例阈值(例如%1),重试次数占所有请求比例不超过该阈值。
30.png
防止链路重试
链路层面的防重试风暴的核心是限制每层都发生重试,理想情况下只有最下一层发生重试。可以返回特殊的status表明“请求失败,但别重试”。
31.png
Hedged requests
对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应。
32.png
4.4 重试效果验证
实际验证经过上述重试策略后,在链路上发生的重试放大效应。
33.png
4.5 总结
- 重试的意义及难点
- 应对重试风暴的策略
课程总结
- 微服务架构介绍
- 微服务架构原理及特征
- 核心服务治理功能
- 字节跳动服务治理实践