都是套路:高并发系统的降级特技

标签: 运维经验 高并发系统 | 发表时间:2016-07-28 12:03 | 作者:小码哥
出处:http://www.yunweipai.com

作者简介:

张开涛

京东集团技术研发,2014年加入京东,开发过京东商品详情页、详情页统一服务架构与开发工作,设计并开发了多个亿级访问量系统。

工作之余喜欢写技术博客,有《跟我学 Spring》、《跟我学Spring MVC》、《跟我学Shiro》、《跟我学Nginx+Lua开发》等系列教程。

开篇:

在开发高并发 系统时有三把利器用来保护系统:缓存、降级和限流。之前已经有一些文章介绍过缓存和限流了。本文将详细聊聊降级。

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。

系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。本文将介绍一些笔者在实际工作中遇到的或见到过的一些降级方案供大家参考。

降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

降级预案

在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;

警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;

错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;

严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

降级的类别

  • 降级按照是否自动化可分为:自动开关降级和人工开关降级。
  • 降级按照功能可分为:读服务降级、写服务降级。
  • 降级按照处于的系统层次可分为:多级降级。

降级的功能点

降级的功能点主要从服务端链路考虑,即根据用户访问的服务调用链路来梳理哪里需要降级:

页面降级:在大促或者某些特殊情况下,某些页面占用了一些稀缺服务资源,在紧急情况下可以对其整个降级,以达到丢卒保帅;

页面片段降级:比如商品详情页中的商家部分因为数据错误了,此时需要对其进行降级;

页面异步请求降级:比如商品详情页上有推荐信息/配送至等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进行降级;

服务功能降级:比如渲染商品详情页时需要调用一些不太重要的服务:相关分类、热销榜等,而这些服务在异常情况下直接不获取,即降级即可;

读降级:比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景;

写降级:比如秒杀抢购,我们可以只进行Cache的更新,然后异步同步扣减库存到DB,保证最终一致性即可,此时可以将DB降级为Cache。

爬虫降级:在大促活动时,可以将爬虫流量导向静态页或者返回空数据从而降级保护后端稀缺资源。

降级策略

1、自动开关降级

自动降级是根据系统负载、资源使用情况、SLA等指标进行降级。

超时降级

当访问的数据库/http服务/远程调用响应慢或者长时间响应慢,且该服务不是核心服务的话可以在超时后自动降级;

比如商品详情页上有推荐内容/评价,但是推荐内容/评价暂时不展示对用户购物流程不会产生很大的影响;

对于这种服务是可以超时降级的。如果是调用别人的远程服务,和对方定义一个服务响应最大时间,如果超时了则自动降级。

之前总结过一些的文章《使用httpclient必须知道的参数设置及代码写法、存在的风险》和《dbcp配置及jdbc超时设置总结》。在实际场景用一定主要配置好超时时间和超时重试次数和机制。

统计失败次数降级

有时候依赖一些不稳定的API,比如调用外部机票服务,当失败调用次数达到一定阀值自动降级;然后通过异步线程去探测服务是否恢复了,则取消降级。

故障降级

比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。

降级后的处理方案有:

默认值(比如库存服务挂了,返回默认现货)

兜底数据(比如广告挂了,返回提前准备好的一些静态页面)

缓存(之前暂存的一些缓存数据)

限流降级

当我们去秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时开发者会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;

降级后的处理方案可以是:

排队页面(将用户导流到排队页面等一会重试)

无货(直接告知用户没货了)

错误页(如活动太火爆了,稍后重试)

2、人工开关降级

  • 在大促期间通过监控发现线上的一些服务存在问题,这个时候需要暂时将这些服务摘掉;
  • 还有有时候通过任务系统调用一些服务,但是服务依赖的数据库可能存在:网卡被打满了、挂掉了或者很多慢查询,此时需要暂停下任务系统让服务方进行处理;
  • 还有发现突然调用量太大,可能需要改变处理方式(比如同步转换为异步);

此时就可以 使用开关来完成降级

开关可以存放到配置文件、存放到数据库、存放到Redis/ZooKeeper;如果不是存放在本地,可以定期同步开关数据(比如1秒同步一次)。然后通过判断某个KEY的值来决定是否降级。

另外对于新开发的服务想上线进行灰度测试;但是不太确定该服务的逻辑是否正确,此时就需要设置开关,当新服务有问题可以通过开关切换回老服务。

还有多机房服务,如果某个机房挂掉了,此时需要将一个机房的服务切到另一个机房,此时也可以通过开关完成切换。

还有一些是因为功能问题需要暂时屏蔽掉某些功能,比如商品规格参数数据有问题,数据问题不能用回滚解决,此时需要开关控制降级。

3、读服务降级

对于读服务降级一般采用的策略有:

暂时切换读(降级到读缓存、降级到走静态化)

暂时屏蔽读(屏蔽读入口、屏蔽某个读服务)

在《应用多级缓存模式支撑海量读服务》中曾经介绍过 读服务,即:

接入层缓存→应用层本地缓存→分布式缓存→RPC服务/DB

我们会在接入层、应用层设置开关,当分布式缓存、RPC服务/DB有问题自动降级为不调用。当然这种情况适用于对读一致性要求不高的场景。

页面降级、页面片段降级、页面异步请求降级都是读服务降级,目的是丢卒保帅(比如因为这些服务也要使用核心资源、或者占了带宽影响到核心服务)或者因数据问题暂时屏蔽。

还有一种是页面静态化场景:

动态化降级为静态化:比如平时网站可以走动态化渲染商品详情页,但是到了大促来临之际可以将其切换为静态化来减少对核心资源的占用,而且可以提升性能;其他还有如列表页、首页、频道页都可以这么玩;可以通过一个程序定期的推送静态页到缓存或者生成到磁盘,出问题时直接切过去;

静态化降级为动态化:比如当使用静态化来实现商品详情页架构时,平时使用静态化来提供服务,但是因为特殊原因静态化页面有问题了,需要暂时切换回动态化来保证服务正确性。

以上都保证出问题了有预案,用户还是可以使用网站,不影响用户购物。

4、写服务降级

写服务在大多数场景下是不可降级的,不过可以通过一些迂回战术来解决问题。比如将同步操作转换为异步操作,或者限制写的量/比例。

比如扣减库存一般这样操作:

方案1:

a、扣减DB库存;

b、扣减成功后更新Redis中的库存;

方案2:

a、扣减Redis库存;

b、同步扣减DB库存,如果扣减失败则回滚Redis库存;

前两种方案非常依赖DB,假设此时DB性能跟不上则扣减库存就会遇到问题;因此我们可以想到 方案3:

a、扣减Redis库存:

b、正常同步扣减DB库存,性能扛不住时降级为发送一条扣减DB库存的消息,然后异步进行DB库存扣减实现最终一致即可;

这种方式发送扣减DB库存消息也可能成为瓶颈;这种情况我们可以考虑 方案4:

a、扣减Redis库存;

b、正常同步扣减DB库存,性能扛不住时降级为写扣减DB库存消息到本机,然后本机通过异步进行DB库存扣减来实现最终一致性。

也就是说正常情况可以同步扣减库存,在性能扛不住时降级为异步;另外如果是秒杀场景可以直接降级为异步,从而保护系统。

还有如下单操作可以在大促时暂时降级将下单数据写入Redis,然后等峰值过去了再同步回DB,当然也有更好的解决方案,但是更复杂,不是本文的重点。

还有如用户评价,如果评价量太大,也可以把评价从同步写降级为异步写。当然也可以对评价按钮进行按比例开放(比如一些人的看不到评价操作按钮)。比如评价成功后会发一些奖励,在必要的时候降级同步到异步。

5、多级降级

缓存是离用户最近越高效;而降级是离用户越近越能对系统保护的好。因为业务的复杂性导致越到后端QPS/TPS越低。

页面JS降级开关:主要控制页面功能的降级,在页面中通过JS脚本部署功能降级开关,在适当时机开启/关闭开关;

接入层降级开关:主要控制请求入口的降级,请求进入后会首先进入接入层,在接入层可以配置功能降级开关,可以根据实际情况进行自动/人工降级;

这个可以参考《京东商品详情页服务闭环实践》,尤其在后端应用服务出问题时,通过接入层降级从而给应用服务有足够的时间恢复服务;

应用层降级开关:主要控制业务的降级,在应用中配置相应的功能开关,根据实际业务情况进行自动/人工降级。

总结:

降级能保障系统在大促中活下来,而不是死去,达到丢卒保帅的作用。对用户提供有损服务,总比不服务要好。根据自己的场景设计相应的降级策略,保障系统在危机时刻能通过降级手段平稳度过。

文/张开涛

文章出处:高效运维

相关 [套路 并发 系统] 推荐:

都是套路:高并发系统的降级特技

- - 运维派
京东集团技术研发,2014年加入京东,开发过京东商品详情页、详情页统一服务架构与开发工作,设计并开发了多个亿级访问量系统. 工作之余喜欢写技术博客,有《跟我学 Spring》、《跟我学Spring MVC》、《跟我学Shiro》、《跟我学Nginx+Lua开发》等系列教程. 在开发高并发 系统时有三把利器用来保护系统:缓存、降级和限流.

高并发系统之限流特技

- - 编程语言 - ITeye博客
在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流. 缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流.

高并发系统设计思路

- - 掘金 后端
不管是哪一门语言,并发都是程序员们最为头疼的部分. 同样,对于一个软件而言也是这样,你可以很快增删改查做出一个秒杀系统,但是要让它支持高并发访问就没那么容易了. 如何让系统面对百万级的请求流量不出故障. 如何保证高并发情况下数据的一致性写. 作为一个架构师,首先要勾勒出一个轮廓,如何构建一个超大流量并发读写、高性能,以及高可用的系统,这其中有哪些要素需要考虑.

构建高性能高并发Java系统

- scourgen - ITeye博客
异步通信显然可以更快的返回响应. 从实际经验看,对高吞吐服务器更大的好处是,系统中的某一服务出现问题后往往出现雪崩似的服务宕机. 这很多都是由于采用同步通信,需要等待其他服务同步通信结束后,其占用资源才能得到释放. 而这些资源往往是socket连接、线程、数据库连接等比较重的资源. 如果你真的需要他,可以用个mock同步.

多核系统上的 Java 并发缺陷模式(bug patterns)

- yat - IBM developerWorks 中国 : 文档库
通过研究并发(bug patterns)缺陷模式,您既能够提高对并发编程的理解,还能够了解如何发现无效或可能无效的编程方法. 在本文中,作者 Zhi Da Luo、Yarden Nir-Buchbinder 和. Raja Das 阐述了 6 个鲜为人知的、可能威胁运行在多核系统上的 Java 应用程序的线程安全和性能的并发缺陷.

简单的搭建一个高并发低时延系统

- - 博客园_首页
首先声明一点:这里的“高并发”是相对的,相对于硬件而言,而不是绝对的高并发. 后者需要分布式来实现,这里不做讨论. 最近在做一个语音通信系统,要求在线用户2W,并发1K路通话. 硬件是两台服务器,酷睿多核,4G内存,千兆网卡(我用过的最好的硬件,负担这些应该问题不大). 系统的另一个指标是呼叫时延和语音时延.

大流量、高并发的网站的底层系统架构

- - 企业架构 - ITeye博客
大流量、高并发的网站的底层系统架构. [转载自] http://www.webjx.com/webmanage/experience-25319.html. 动态应用,是相对于网站静态内容而言, 是指以c/c++、php、Java、perl、.net等 服务器端语言开发的网络应用软件,比如论坛、网络相册、交友、BLOG等常见应用.

高并发服务端分布式系统设计概要

- - 博客 - 伯乐在线
写这篇文章的目的,主要是把今年以来学习的一些东西积淀下来,同时作为之前文章《 高性能分布式计算与存储系统设计概要》的补充与提升,然而本人水平非常有限,回头看之前写的文章也有许多不足,甚至是错误,希望同学们看到了错误多多见谅,更欢迎与我讨论并指正. 我大概是从2010年底起开始进入高并发、高性能服务器和分布式这一块领域的研究,到现在也差不多有三年,但其实很多东西仍然是一知半解,我所提到的许许多多概念,也许任何一个我都不能讲的很清楚,还需要继续钻研.

Web系统大规模并发——电商秒杀与抢购

- - 行业应用 - ITeye博客
电商的秒杀和抢购,对我们来说,都不是一个陌生的东西. 然而,从技术的角度来说,这对于Web系统是一个巨大的考验. 当一个Web系统,在一秒钟内收到数以万计甚至更多请求时,系统的优化和稳定至关重要. 这次我们会关注秒杀和抢购的技术实现和优化,同时,从技术层面揭开,为什么我们总是不容易抢到火车票的原因.

聊聊高并发系统之HTTP缓存(转)

- - 企业架构 - ITeye博客
原文网址:http://jinnianshilongnian.iteye.com/blog/2319573. 最近遇到很多人来咨询我关于浏览器缓存的一些问题,而这些问题都是类似的,因此总结本文来解答以后遇到类似问题的朋友. 因本文主要以浏览器缓存场景介绍,所以非浏览器场景下的一些用法本文不会介绍,而且本文以chrome为测试浏览器.