这是我参与「第五届青训营 」笔记创作活动的第9天
本节课主要讲解了微服务架构的原理和应用。
1.微服务架构介绍
系统架构演变
graph TD
A(单体架构:性能最高冗余小,但是难以debug,模块相互影响)
B(垂直应用架构:按照业务线垂直划分)
C(分布式架构:抽取业务无关的公共模块,复用公共逻辑来去冗余)
D(SOA架构:面向服务,整个系统通过注册中心来获取服务层的服务)
E(微服务架构:自下而上的独立设计,业务独立设计,故障隔离)
A --> B
B --> C
C --> D
D --> E
微服务架构概览
微服务架构核心要素
- 服务治理:服务注册、发现,负载均衡,扩缩容,流量治理,稳定性治理
- 可观测性:日志采集,日志分析,监控打点,监控大盘,异常报警,链路追踪
- 安全性:身份验证,认证授权,访问令牌,审计,传输加密,黑产攻击
基本概念
- 服务:一组具有相同逻辑的运行实体(即运行同一个代码)
- 实例:一个服务中,每个运行实体即为一个实例
实例与进程之间没有必然对应联系,可以一个实例对应一个或多个进程
- 集群:通常指服务内部的逻辑划分,包含多个实例
- 常见实例承载形式:进程、VM、k8s pod等
- 有状态/无状态服务:服务的实例是否存储了持久化数据
服务间通信:对于单体服务,不同模块通信只是简单的函数调用;对于微服务,服务间通信意味着网络传输。
服务注册和发现
在代码层面,指定调用一个目标服务的地址:使用hardcode
ServiceA wants to call service B.
client := grpc.NewClient(ip)
由于一个服务部署了多个实例在多个端口上,因此不能够写死ip。
ip的调用形式:
1.使用DNS的方式,B服务为每一个实例在不同ip下注册相同端口的DNS
ip := b.service.org:8080
b.service.org={
10.23.45.67
10.45.67.89
10.67.89.12
}
问题:本地DNS有缓存机制,会导致延时,不支持服务实例的探活检查,同时端口限定
2.服务注册中心存储服务名到服务实例的映射
本质上是构建一个服务内部维护的ip容器,按照容器索引选取,非持久化的方式也保证了同步速度和动态变化需求。
ipList := svc_reg.find("service.b")
ip := ipList[random(n)] //使用负载均衡算法选取ip
服务实例上线及下线过程:
- 下线:服务A不断记录对B实例的访问情况,当B某个实例没有流量时进行下线。
- 上线:当服务B发现当前实例压力较大,会对新的实例进行健康检查,通过检查之后即可上线服务注册中心。
流量特征
使用统一的网关入口,内网通信多数采用RPC(HTTP作为文本协议效率不如使用二进制形式传输数据的RPC),使用网状调用链路。
3.核心服务治理功能
服务发布
服务发布是让一个服务升级运行新的代码的过程
服务发布的难点
- 服务不可用:服务B升级导致服务A无法调用服务B
- 服务抖动:服务B升级导致不可预测的错误出现
- 服务回滚:B服务发现问题回滚时无法被服务A调用
蓝绿部署
先将流量全部导入蓝色集群,升级绿色集群;再将流量全部导入绿色集群,升级蓝色集群,然后恢复
缺点:两倍资源占用
灰度发布
灰度发布也称为金丝雀发布,思路是逐次上线新服务,并且迭代掉旧的服务
缺点:需要精细导入流量,且迭代程度越高回滚难度越大
流量治理
在微服务架构下,可以基于地区、集群、实例、请求等维度,对端到端的流量的路由路径进行分配
负载均衡
负载均衡(Load Balance)负责分配请求在每个下游实例上的发布
常见的LB策略:
- Round Robin
- Random
- Ring Hash
- Least Request
稳定性治理
线上服务总是会出问题的,这与程序的正确性无关
稳定性治理功能:
- 限流
- 熔断
- 过载保护
- 降级
字节跳动服务治理实践
重试的意义
本地函数调用
可能的异常:参数非法、OOM(Out Of Memmory)、NPE(Null Pointer Exception)、边界条件、系统崩溃、死循环、程序异常退出
func LocalFunc(x int) int{
res := calculate(x * 2)
return res
}
本地的函数调用通常没有重试的意义
远程函数调用
func RemoteFunc(ctx content.Content, xint) (int error){
ctx2, defer_func := context.WithTimeout(ctx, time.Second) //设置一秒超时
defer defer func()
res, err := grpc_client.Calculate(ctx2, x * 2)
return res, err
}
可能的异常:网络抖动,下游负载高导致超时,下游机器宕机,本地机器负载高,调度超时,下游熔断、限流
意义
- 降低错误率
- 降低长尾延时,对于偶尔耗时较长的请求,重试请求有机会提前返回
- 容忍暂时性错误:规避系统的暂时性异常
- 避开下游故障实例:一个服务中可能会有少量的实例故障,重试其他实例可以成功
重试的难点
- 幂等性
- 重试风暴
- 超时设置
重试策略
- 限制重试比例:设定一个重试比例阈值(例如1%),重试次数占所有请求比例不超过该阈值
- 防止链路重试:链路层面防止重试风暴的核心是限制每层都发生重试,理想情况下只有最下一层发生重试,可以返回特殊的status表明“请求失败,但别重试”。
- Hedged requests:对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应。
总结
本节课程主要介绍了微服务架构及其相互之间通信、实例的更新发布、流量治理和重试。整体上涵盖了微服务全部内容的关键模块,也为微服务性能的调优提供了很多思路。