这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记
1.微服务架构演变历史
单体架构 -》 垂直应用架构 -》 分布式架构 -》 SOA架构 -》 微服务架构
1.1 微服务架构核心要素
2.微服务架构原理及特征
2.1基本概念
服务:一组具有相同逻辑的运行实体
实例:一个服务中,每个运行实体就是一个实例。比如将你写的代码运行起来,就是一个实例,运行部署多次就是多个实例
实例与进程的关系:没有必然联系,通常是一个实例对应一个或多个进程(反之不常见)
集群:通常指服务内部的逻辑划分,包含多个实例
常见的实例承载方式:进程、JVM、k8s pod等
有状态、无状态服务:服务的实例是否存储了可持久化的数据。
服务包含集群,集群包含多个实例
服务间通信
对于单体服务来说,不同模块通信只是简单的函数调用
对于微服务来说,服务间意味着网络传输(HTTP、Thrift、gRPC等协议)
2.2 服务注册及发现
微服务架构中,各服务间采用网络传输的方式进行调用,必然需要指定被调用方的ip和端口,下面是三种指定下游实例地址的方式
- hardcode方式:直接在调用方写死被调用方地址
存在问题:一个服务有多个实例,且其ip和端口是动态变化的,这种方式并不可取
- DNS:一个域名映射多个ip,调用方用过域名来调用
存在问题:
1. 本地DNS存在缓存,导致延时
2. 负载均衡问题
3. 没有服务实例的探活检查
4. 域名无法配置端口
- 服务注册中心,在调用方和被调用方之间引入一个中间层,用来存储服务名到服务实例的映射
最终的解决方案,
2.3 流量特征
- 统一网关入口
- 内网通信多采用RPC
- 网状调用链路
3.核心服务治理功能
3.1 服务发布
概念:指一个服务升级运行新的代码的过程
难点:
- 服务不可用:服务在发布时,若采用将所有旧服务down掉,然后发布新服务的方式,影响很大,用户体验极差
- 服务抖动:每次升级一部分服务实例,出现服务暂时不可用
- 服务回滚:与我们平时在本地编写代码不同,线上环境,出现故障的第一反应该是止损,然后再排查故障。服务回滚是一种解决方式。
两种服务发布方式:
- 蓝绿部署:服务发布时,将该服务中的实例分为两半,每次完成一半实例的部署,另一半承担所有流量
优点:简单且稳定,适用于流量低的时候
缺点:消耗资源,需要两倍的资源来完成
- 灰度发布:首先创建一个新实例(升级后的),线上测试,若没问题,就下线掉一个旧实例,然后继续上线一个新实例,逐步替换所有旧实例。
缺点:回滚难度大,基础设施要求高
3.2 服务治理
基于地区、集群、实例、请求等维度,对端到端流量的路由路径进行精确控制
3.3 负载均衡
负责分配请求在每个下游实例上的分布
常见的负载均衡策略:
- Round Robin(轮询):将请求按顺序轮流地分配到服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
- Random(随机法):通过随机算法从服务器列表中随机选取一台服务器进行访问。
- Ring Hash(一致性哈希法):根据请求来源的地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。
- Least Request(最小连接数法):根据服务器当前的连接情况进行负载均衡的,当请求到来时,会选取当前连接数最少的一台服务器来处理请求。
3.4 稳定性治理
减少与程序正确性无关的突发情况的出现对服务稳定性的影响
- 限流:限制流量,拒绝多余请求
- 熔断:若服务A请求服务B,出现连接失败,服务A会直接拒绝其他请求服务B的请求,也不会不断重试,而是间断性的尝试连接。
- 过载保护:若服务压力过大时,可以拒绝一部分请求,使得服务慢慢恢复
- 降级:若流量过大,导致服务负荷过大,可以选择重要请求执行,拒绝其他请求,防止服务崩溃
4.字节服务治理实践----重试
服务调用失败后 重试 可以避免偶发的错误,提高SLA
意义:
- 降低错误率
- 降低长尾延时:一些请求可能因为一些原因,导致响应比平时慢很多,而重试请求就有机会提前返回
- 容忍暂时性错误
- 避免下游故障实例:少量实例出现故障,重试请求可能会访问到其他正常实例
难点:
- 幂等性:多次请求可能会造成数据不一致
- 重试风暴:随着调用深度的增加,重试次数会指数级上涨。雪崩
- 超时设置:如何才能合理的判断这次请求失败。
解决重试风暴的方法:
-
限制重试比例
- 重试只有在大部分请求都成功,只有少量请求失败时,才有必要。如果大部分请求都失败,重试只会加剧问题严重性
-
防止链路重试
- 链路层面的防重试风暴的核心是限制每层都发生重试,理想情况下只有最下一层发生重试。 可以返回特殊的 status 表明“请求失败,但别重试”。
-
Hedged requests
- 对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应。