服务可用性治理
当微服务A是微服务B的调用方时,我们称B是A的下游服务,而A是B的上游服务
可用性可以从两个角度出发:
- 容错性设计:要接受网络脆弱与下游服务质量不可靠的事实,并在进行服务设计时充分考虑相应故障产生时的容错方案。如重试、降级
- 流量控制:既要对上游服务调用采取预防性策略,防止打垮我们的服务;也要对下游服务有感知与保护意识,当感知到下游服务的质量下滑甚至服务不可用时, 及时通过自身保护下游服务。如熔断、隔离、限流
因为一个服务在系统中,是同时拥有上游和下游的,所以在讨论接下来的东西时,需要同时考虑两种角色。
重试
幂等接口:
当服务作为下游,被上游重复调用时,需要保证接口幂等,实现方式有多种:
- 数据库层面采用CAS版本号进行更新、唯一键检查防多次插入等方式
- Redis分布式锁+幂等标识位+过期时间
- 数据库防重表,利用数据库的唯一索引冲突
- 上游先向下游申请token,下游生成并在redis存储token,上游携带token发起请求,下游删除redis中的token,如果删除成功,则执行,删除失败,则不执行。下游需要把握删除token和执行结果的一致性。

重试时机:
提高单次请求的成功率。
不是什么时候都应该重试,当出现错误时,可能有多种情况:
- 业务逻辑错误,即下游服务认为请求不符合业务逻辑而返回的错误。比如下游服务在处理扣款请求时发现用户余额不足,或者某请求包含非法参数,这些都属于业务逻辑错误。
- 服务质量异常错误,即反映下游服务稳定性的相关错误。比如下游服务对请求限流、下游服务拒绝提供服务,或者由于调用下游服务失败率过高请求被熔断,这些都属于服务质量异常错误。
- 网络错误,如请求超时、数据丢包、网络抖动、连接断开等
重试时机也很重要:
- 无退避策略:请求失败后立即重试
- 线性退避策略:每次请求失败后都等待固定的时间重试。
- 随机退避策略:在一个时间范围内随机选取一个时间等待重试。
- 指数退避策略:对一个请求连续重试时,每次等待的时长都是上一次的2倍。
- 综合退避策略:可以是指数退避策略与随机退避策略结合的形式,各开源消息中间件在应对消息消费失败时常使用此策略。
当RPC接口调用遇到业务逻辑错误时不应该重试请求,因为此请求涉及的业务逻辑有异常,无论重试多少次都是一样的结果;
当RPC接口调用遇到服务质量异常错误时, 由于服务质量处于异常状态是有一定的持续时间的,使用无退避策略(立即重试请求)会大概率继续请求失败,所以此时适合使用各种有退避的策略,比如指数退避策略,这样可以在一定程度上给足下游服务质量恢复的时间;
当RPC接口调用遇到网络错误时,因为网络错误具有随机性,重试请求再次失败的概率很小,所以可以在下游服务未熔断的前提下立即重试请求。
重试风暴:
重试应该限定次数,不能无限次重试,但即使有限制,比如最多重试三次,也可能产生重试风暴:

这里只举例说明了具有3个微服务的简单架构遇到的重试风暴,而在真实的互联网微 服务架构环境中,一个请求经过数十个微服务是很常见的,此时如果某个环节出现故障, 那么重试风暴造成的请求量放大更加可怕。
重试控制:
对于一些不应该重试的服务,可以不重试:
- 非关键下游服务,比如查询用户主页,用户的IP归属地并不是关键信息,获取失败可以不重试
- 上游服务的重试请求,这是为了避免重试风暴,这个策略需要在请求头携带一个标记
- 服务质量异常错误:如果受到下游服务限流,或者下游服务已经被熔断,那么也不该重试了
控制重试比:
比如当滑动窗口某段时间内重试请求低于正常请求的10%时,才被允许重试
熔断
上游服务保护下游服务不被打垮。
服务雪崩 :
由于网络原因或服务自身设计问题,每个微服务一般都难以保证100%对外可用。如 果某服务出现了质量问题,那么与其相关的上游服务网络调用就容易出现线程阻塞的情 况;如果有大量的线程发生阻塞,则会导致上游服务承受较大的负载压力而发生宕机故障。 在微服务架构中,由于在服务间建立了依赖关系,所以一个服务的故障会不断向上传播, 最终导致整个服务链路发生宕机故障。这就是服务雪崩现象。
熔断器:

隔离
防止下游服务调用相互影响。
线程池隔离:
假设下游服务的调用用同一个线程池来共享调用线程资源,比如下游有服务B、C、D,共享线程池大小150,假使服务B耗时较长,随着时间推移,服务B会占用更多线程资源。
信号量隔离:
如果调用的下游服务变多,线程池数量和线程数量也会变多,这时候开销就上来了,这时可以针对每个服务进行信号量隔离,比如B服务设置50个信号量,那么最多可以有50个并发调用
限流
下游服务保护自己不被上游服务打垮。
降级
保障服务核心功能的可用性,具体的实施方案比较灵活
服务依赖度降级:
应对大流量时,优先关闭低依赖的服务调用,保证核心功能可用
读请求降级:
如果访问某数据失败,可以降级为访问另一份基本可用的数据,可以是静态数据,也可以是另一份数据源的数据
比如直播打赏礼物列表,可以事先在网关设置一些常见的礼物数据。或者是离线生成一些数据供调用
写请求降级:
比如弹幕场景,可以在客户端直接展示用户弹幕,让用户觉得自己发送成功了,然后按照一定比例先在客户端丢弃一部分弹幕,然后服务端再按照一定比例把弹幕扩散给部分用户

2940

被折叠的 条评论
为什么被折叠?



