服务降级
我们在开发高并发系统时有三把利器来保护系统:缓存,降级和限流。这边文章来重点聊一聊降级。当访问量剧增,服务出现问题(响应时间慢或者不响应)或者非核心业务影响到核心流程时,仍然需要保证服务是可用的,即使是有损的服务。
系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。下面提供几种实际工作中遇到的或者见到的一些降级方案。
降级的最终目的是保证核心服务的可用性,即使是有损的。有些服务是无法降级的(如加入购物车,结算)。
降级预案
需要对系统进行梳理,哪些是可以降级的,哪些是需要誓死保护的,可以参考日志级别。
一般:有些服务因为网络抖动或者服务上线而超时,可以自动降级。
警告:有些服务成功率有波动(95%-100%之间),可以自动降级或人工降级,并发送警告。
错误:比如错误率低于 90%,或者数据库连接池爆了,或者访问量猛增到系统承受的最大阀值,可以根据情况自动降级或者人工降级。
严重错误:特殊原因数据错误,此时需要紧急人工降级。
降级的功能点主要从服务端链路考虑,即根据用户访问的服务调用链路来梳理哪里需要降级:
页面降级:在大促或者某些特殊情况下,某些页面占用了一些稀缺服务资源,在紧急情况下可以对其整个降级,以达到丢卒保帅;
页面片段降级:比如商品详情页中的商家部分因为数据错误了,此时需要对其进行降级;
页面异步请求降级:比如商品详情页上有推荐信息/配送至等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进行降级;
服务功能降级:比如渲染商品详情页时需要调用一些不太重要的服务:相关分类、热销榜等,而这些服务在异常情况下直接不获取,即降级即可;
读降级:比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景;
写降级:比如秒杀抢购,我们可以只进行Cache的更新,然后异步同步扣减库存到DB,保证最终一致性即可,此时可以将DB降级为Cache。
爬虫降级:在大促活动时,可以将爬虫流量导向静态页或者返回空数据,从而保护后端稀缺资源。
自动开关降级
自动降级是根据系统负载,资源使用情况,SLA 等指标进行降级。
超时降级
当访问的数据库/http服务/远程调用响应慢或者不响应可以设置超时自动降级。
统计失败次数降级
有时候依赖一些不稳定的API,比如调用外部机票服务,当失败调用次数达到一定阀值自动降级;然后通过异步线程去探测服务是否恢复了,则取消降级。
故障降级
当调用的远程服务挂掉了(网络故障,DNS 故障,http 服务返回错误的状态码,RPC 服务抛出异常),则可以直接降级。
限流降级
当我们去秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时开发者会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
人工开关降级
在大促期间通过监控发现线上的一些服务存在问题,这个时候需要暂时将这些服务摘掉。
读服务降级
对于读服务降级一般采用的策略有:暂时切换读(降级到读缓存、降级到走静态化)、暂时屏蔽读(屏蔽读入口、屏蔽某个读服务)。
写服务降级
写服务在大多数场景下是不可降级的,不过可以通过一些迂回战术来解决问题。比如将同步操作转换为异步操作,或者限制写的量/比例。
比如扣减库存一般这样操作:
方案1:
1、扣减DB库存,2、扣减成功后更新Redis中的库存;
方案2:
1、扣减Redis库存,2、同步扣减DB库存,如果扣减失败则回滚Redis库存;
前两种方案非常依赖DB,假设此时DB性能跟不上则扣减库存就会遇到问题;因此我们可以想到方案3:
1、扣减Redis库存,2、正常同步扣减DB库存,性能扛不住时降级为发送一条扣减DB库存的消息,然后异步进行DB库存扣减实现最终一致即可;
这种方式发送扣减DB库存消息也可能成为瓶颈;这种情况我们可以考虑方案4:
1、扣减Redis库存,2、正常同步扣减DB库存,性能扛不住时降级为写扣减DB库存消息到本机,然后本机通过异步进行DB库存扣减来实现最终一致性。