09 容量场景(二):一次让网盘雪崩的热点事件
你好,我是白园。
上节课我分享了关于解决容量问题的直接两个手段, 控制流量和增加资源,除了这两个办法之外,还有哪些场景和解决方案呢?今天我就想通过2015年的一个案例来聊聊这个话题。
当时的情况是这样的,一部热门电视剧在网络上迅速传播,成为热点事件。由于短时间内大量用户下载该视频,最终导致百度网盘雪崩。今天我把这个故障的经验和教训分享给你,希望你能从中学到如何处理热点事件,如何处理雪崩。
网盘的架构设计
在深入探讨故障之前,我先简要介绍一下当时(2015年)网盘的架构设计,以便你更好地理解故障发生的根本原因。
网盘架构主要分为四个层次:
- 客户端: 主要是移动端和PC端两种。
- 产品层:负责用户信息的管理工作,包括用户注册、登录、权限分配等。
- 中间层:承担文件管理的职责,包括文件命名、格式处理、元数据维护等。
- 存储层:作为数据的最终存储地,采用了百度自建的存储系统,即百度云存储(BOS)的前身,为网盘提供强大的数据存储能力。
故障的起因是某部热门电视剧更新后,有人把它上传到网盘并进行了公开分享。随后,很多用户开始迅速下载该文件,导致存储层先出现了问题。在网盘的日常使用中,大多数文件为私有文件,通常单个文件的下载次数较少,大约在1~2次。然而,在这种情况下,热点文件在短时间内的下载次数激增至数百次,导致磁盘I/O过载,形成了局部热点现象,显著延长了文件的下载时间。
故障原因
首先是热点视频大量下载导致了存储的IO过载,开始下载失败,但上面的客户端、产品层、中间层并不了解存储层的故障原因,因而持续尝试重试,这加剧了问题的扩散。当时的架构主要基于PHP文件,而PHP的线程处理能力是有限的。一旦线程被分配用于处理特定文件的下载,它就会变得不可响应。在这种情况下,由于大量用户同时尝试下载,导致所有可用的PHP线程迅速耗尽,进而使系统响应能力大幅下降。
同时,文件管理层的PHP服务不仅负责处理下载请求,还承担着文件展示、上传、重命名等多种功能。由于这一服务的故障,用户无法对网盘文件进行任何操作,造成了大范围的服务中断和显著的负面影响。 这个时候引发了更大的故障,端上、产品层、中间层都在重试,成倍地重试引发了全面故障。端上默认重试5次,产品层3次,中间层3次,这样一次下载失败就会重试45次。
故障恢复
你可以看一下当时的故障损失图 。
22:45 故障出现
23:00 开始全面雪崩,为了恢复服务,我们采取了一些措施。
23:05 在上游实施全面的限流措施,并封禁所有用户的文件操作,以减轻系统压力。一开始尝试重启,但服务起不来,一起来就挂,所以只能做全面的限流和封禁,因为有大量重拾,限流基本会限制死,直接在接入层返回失败。
23:10 重启了中间层的PHP服务,使其恢复正常运行。在接入层限死之后,这个时候中间层的PHP还属于重试和夯死的状态,需要等超时时间结束之后才能恢复。
23: 20 重启存储层中所有受影响的服务,并确保文件的可用性。存储层服务中这时还没有完全恢复,因此做了局部重试,之后存储层完成了恢复。
23:30 手动下载并推送至内容分发网络(CDN),这时用户的下载还在,因为文件还没有下载到本地。我们手动下载了热点文件,并把它推送到CDN,后续用户的下载请求可以直接从CDN获取,减轻网盘的直接负载。
23:40 逐步解除限流和封禁,直至完全恢复服务。
这里其实有两个关键问题, 一是热点如何避免,二是雪崩如何避免和恢复。
热点治理
在刚刚的故障案例中,我们主要做了四点优化,限流、缓存、隔离、调整CDN推送策略。
为了应对类似热点事件,我们先实施针对单个文件的流量控制策略。当检测到某个文件在短时间内出现大量下载请求时,系统自动增加延迟并限制下载速度,同时向用户发出提示,建议他们稍后再尝试下载。
为了提高系统的响应能力并保护底层存储,我们在存储层之上引入了一层缓存机制,使用的是SSD(固态硬盘)作为缓存介质。用户的下载请求首先会通过这层SSD缓存来处理,而缓存优先存储最近被频繁访问的文件。这一措施不仅加快了用户下载速度,同时也减轻了底层存储的压力。由于成本的压力,网盘的存储层用的是高密度大存储的磁盘,读写性能非常低。
另外,我们还增设了一个热点和灾备集群,旨在主动管理热点文件。当系统检测到某个文件成为热点时,会自动将其流量切换至热点集群处理,从而避免热点文件对系统中其他文件的负面影响,确保整个系统的稳定性和性能。
为了更精确地控制内容分发,我们引入了一套更加严格的推送至CDN的机制和策略。具体来说,系统会实时监测文件的下载活动,一旦在任意1分钟的时间段内,下载请求的数量超过预设的阈值,系统就会自动触发将该文件推送至CDN,以便更高效地处理大量的下载需求。
雪崩治理
在应对雪崩效应的治理中,我们采取了三个核心策略:首先,防止无脑重试,即避免盲目地恢复服务而加剧问题;其次,控制影响范围,通过限流和隔离手段,减少故障对系统其他部分的冲击;最后,实现快速恢复,通过有效的故障定位和修复机制,迅速恢复服务的稳定性和可用性。
控制重试次数
我们面临的问题在于,当下游服务因容量不足而发生故障时,上游服务并未察觉。上游服务仅仅能感知到请求失败,由于缺乏对下游服务状态的感知,它无法识别底层服务的实际容量。因此,上游服务持续不断地进行重试,这反而加剧了问题。
为了解决这一问题,我们实施了一个解决方案,即 通过定义一套协议化的错误码来控制重试行为。当下游服务因容量不足而无法处理请求时,它会返回一个特定的错误码给上游服务。这个错误码作为信息传递的媒介,确保上游服务能够及时识别并响应下游服务的状态。我们定义的错误码为577。一旦上游服务,包括端上、产品层和中间层接收到错误码577,它们会立即停止重试,从而避免了无效重试的蔓延和系统资源的浪费。
避免影响扩大
这里最关键的手段进行隔离,在进行系统隔离部署时,我们遵循一个关键原则—— 适度隔离。过度隔离可能会对后续的运维、上线和资源调度带来不利影响。基于这一原则,我们实施了三层隔离策略。
- 文件列表:首先是展示列表接口的隔离,这一部分操作不依赖于存储层,因此相对独立且易于管理。
- 上传下载:上传和下载操作需要与存储层交互,因此构成了第二层隔离。
- 非核心业务:最后是一些非核心功能,如云解压等,这些被划分到第三层隔离中,进一步优化资源分配和系统管理。
快速恢复
通过这次的事件,我们总结了雪崩效应的恢复流程,并将其自动化以提高效率。首先,我们实施 全面限流 措施,以阻止过量流量涌入系统,减轻压力。紧接着,我们 迅速重启关键服务,以恢复系统的基本运作。一旦服务稳定,我们 逐步恢复流量,确保系统能够平稳地过渡到正常运行状态。
后续,我们开发了一套自动化工具,实现了3个功能: 一键限流,在检测到潜在的雪崩效应时,能够立即启动限流措施; 快速重启, 自动重启关键服务,减少人工干预; 分布引流,在确认服务已恢复稳定后,自动控制流量恢复,避免过快放开导致的冲击。
在后续几次热点事件中,我们采取了分层应对策略。
- 第一层应对:当热点首次出现时,我们迅速将影响范围限制在上传和下载操作上,并将热点文件快速转移到专门的热点集群,来进行隔离和处理。
- 第二层优化:我们引入了缓存层(Cache),这一措施有效抵御了大部分的热点流量,减轻了对系统的冲击。
- 第三层恢复:在另一次由于不同问题导致的雪崩效应中,我们快速切换,把流量导另一个备用集群,实现了服务的快速恢复。
小结
这节课我讲述了我之前经历过的服务雪崩的故障,相信你也能从中总结出一些避坑的方法。为应对热点事件,你可以采取几项措施,比如缓存、限流、灾备等等,在处理雪崩问题时应该注意避免重拾、控制影响以及通过各种工具来快速恢复,具体内容我整理成了表格,你可以参考。
思考题
如果服务发生雪崩,你觉得标准的操作流程是什么样的,是先限流还是先重启?欢迎你把你的答案分享到评论区,也欢迎你把这节课的内容分享给其他朋友,我们下节课再见!