高并发场景三把利器保护系统:缓存、降级、限流。当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。本文将介绍一些笔者在实际工作中遇到的或见到过的一些降级方案供大家参考。
降级按自动化程度分类:
- 系统根据关键数据自动降级;
- 人工通过配置进行手工降级;
自动开关降级指标:
- 超时降级;
- 故障降级;
- 限流降级;
超时降级:
当访问的数据库/http服务/远程调用响应慢或者超时(比如dubbo客户端超时),且该服务不是核心服务的话可以在超时后自动降级;比如商品详情页上有推荐内容/评价,但是推荐内容/评价暂时不展示对用户购物流程不会产生很大的影响;对于这种服务是可以超时降级的。如果是调用别人的远程服务,和对方定义一个服务响应最大时间,如果超时了则自动降级。
故障降级:
比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)。
限流降级:
当我们去秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时开发者会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
手动开关降级:
在大促期间通过监控发现线上的一些服务存在问题,这个时候需要暂时将这些服务摘掉;还有有时候通过任务系统调用一些服务,但是服务依赖的数据库可能存在:网卡被打满了、挂掉了或者很多慢查询,此时需要暂停下任务系统让服务方进行处理;还有发现突然调用量太大,可能需要改变处理方式(比如同步转换为异步);此时就可以使用开关来完成降级。
降级按照功能分类:
- 读服务降级;
- 写服务降级;
读服务降级的思路:
1、开关集中化管理:通过推送机制把开关推送到各个应用(比如我们使用Diamond实现动态配置变更);
2、后端读缓存降级(降级到读缓存、降级到走静态化):比如只读本地缓存、只读分布式缓存、或者只读一个默认的降级数据;
3、前段Nginx请求降级:将开关前置到nginx的接入层,从nginx直接返回一些数据请求,请求打不到后端应用。
举例:
①我们的详情页静态HTML数据默认缓存时间是6min,超过6min就会查询DB进行刷新缓存,如果后端构造详情页的服务挂了,此时缓存又都失效了,就会导致页面出不来,所以我们可以在这里做一个缓存降级策略,如果后端服务挂了,我们是允许使用缓存数据的;用户浏览已经过期的详情页的静态数据比不能浏览要好。
②当一些页面请求量非常大时,一些请求直接在Nginx上做了开关拦截,返回固定数据,让主流程可以不受到影响。
③订单列表页中不同状态订单数查询,流量较大的时候降级为只走缓存;
写服务降级的思路:
写服务在大多数场景下是不可降级的,不过可以通过一些迂回战术来解决问题。比如将同步操作转换为异步操作,或者限制写的量/比例。
举例:
①正常情况可以同步扣减库存,在性能扛不住时降级为异步;另外如果是秒杀场景可以直接降级为异步,从而保护系统。