这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记
一、微服务架构介绍
1.1 微服务架构概览
优势
- 开发效率
- 业务独立设计
- 自下而上
- 故障隔离
劣势
- 治理、运维难度
- 观测挑战
- 安全性
- 分布式系统
1.2 微服务架构核心要素
二、微服务架构原理及特征
2.1 基本概念
- 服务(service): 一组具有相同逻辑的运行实体。
- 实例(instance): 一个服务中,每个运行实体即为一个实例。
- 实例与进程的关系: 实例与进程之间没有必然对应关系,可以一个实例可以对应一个或多个进程(反之不常见)。
- 集群(cluster): 通常指服务内部的逻辑划分,包含多个实例。
- 常见的实例承载形式: 进程、VM、k8spod
- 有状态/无状态服务服务的实例: 是否存储了可持久化的数据(例如磁盘文件)。
- 服务间通信: 对于单体服务,不同模块通信只是简单的函数调用。对于微服务,服务间通信意味着网络传输。
2.2 服务注册与发现
在代码中指定一个目标服务的地址?
- 这个方法不可行,这样所用的请求都会发送到同一台服务器。
使用DNS?
- 本地DNS存在缓存,导致延时。
- 负载均衡问题
- 不支持服务实例的探活检查
- 域名无法配置端口
解决思路: 新增一个统一的服务注册中心,用于存储服务名到服务实例的映射
服务实例上线与显现的过程:
下线:先在注册中心将要下线的服务移除,等待服务的数据请求全部处理完毕后,再下线该服务。
上线:添加一个服务,进行健康检查。证明ok后再注册到注册中心。
2.3 流量特征
- 统一网关入口
- 内网通信多数采用RPC
- 网状调用链路
- rpc调用采用的是二进制传输,http文本传输,所以rpc通信的效率要比http效率高,在内网都用rpc进行服务间的调用。
三、核心服务治理功能
3.1 服务发布
服务发布(deployment)),即指让一个服务升级运行新的代码的过程。
服务发布的难点
在线服务的升级或修改不能对用户的使用造成影响。不能采用正常的线下debug方式进行处理。而是先要进行服务回滚,让系统正常运行。
蓝绿部署
先对绿色进行升级,再将所有流量移到绿色,然后对蓝色升级。
缺点:需要两倍资源,一般在请求量较少的时候进行。
灰度发布
系统中不断加入新的示例,下线旧的示例,最后完成服务的发布。
缺点:要精确控制流量的切换。代码回滚很难。
3.2 流量治理
在微服务架构下,我们可以基于地区、集群、实例、请求等维度,对端到端流量的路由路径进行精确控制。
3.3 负载均衡
负载均衡(Load Balance)负责分配请求在每个下游实例上的分布。
3.4 稳定性治理
线上服务总是会出问题的,这与程序的正确性无关。
- 网络攻击
- 流量突增
- 机房断电
- 光纤被挖
- 机器故障
- 网络故障
- 机房空调故障
微服务架构中典型的稳定性治理功能
3.4.1 限流
打进来5000个qps,serviceB处理不了,限流模块去掉4000个qps。
3.4.2 熔断
当检测到serviceB连接不上的时候,serviceA不会不断重试,而是拒绝上一步的流量。等待serviceB恢复后再连接。
3.4.3 过载保护
检测到serviceB的CPU过载,可以拒绝一部分流量。
3.4.4 降级
保证重要的服务正常服务,等级较低的服务先拒绝。
四、字节跳动服务治理实践
4.1 重试的意义
本地函数的调用
可能有哪些异常?
- 参数非法
- OOM(Out Of Memory)
- NPE (Null Pointer Exception)
- 边界case
- 系统崩溃
- 死循环
- 程序异常退出
远程函数调用(有重试的必要)
可能有哪些异常?
- 网络抖动
- 下游负载高导致超时
- 下游机器宕机
- 本地机器负载高,调度超时
- 下游熔断、限流
重试可以避免掉偶发的错误,提高SLA(Service-Level Agreement)
意义
- 降低错误率:假设单次请求的错误慨率为0.01,那么连续两次错误概率则为0.0001。
- 降低长尾延时:对于偶尔耗时较长的请求,重试请求有机会提前返回。(正常100ms响应的请求有时响应时间会增加到1s)
- 容忍暂时性错误:某些时候系统会有暂时性异常(例如网络抖动),重试可以尽量规避。
- 避开下游故障实例:一个服务中可能会有少量实例故障(例如机器故障),重试其他实例可以成功。
4.2 重试的难点
幂等性:如转账操作的重试带来数据库不一致的问题。
重试风暴
4.3 重试策略
限制重试比例: 设定一个重试比例阈值(例如1%),重试次数占所有请求比例不超过该阈值。
防止链路重试: 链路层面的防重试风暴的核心是限制每层都发生重试,理想情况下只有最下一层发生重试。可以返回特殊的status表明“请求失败,但别重试”。
Hedged requests: 对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应。