如果说访问量上升对网站性能有很大考验,那突发事件,则是检验网站架构的试金石。从某 L 和某 G 谈恋爱,至某 W 和某 M 离婚,到某 F 和某 Z“官宣“事件,都导致某 SNS 网站宕机崩溃,无法为广大网民提供正常服务。互联网圈有一句比较有名谜之反问:
你的网站能承受几个明星出轨?
由此可见,想做好一个网站,远远没有看起来那么简单。
无论你将 WEB 前端进行何种方式的优化,是页面动静分离?各种文件压缩算法?还是将 Web Server 从老牌 Apache 换成性能之王 Nginx(以及其变种)?在绝对的访问量面前,一切都变得脆弱无比,不堪一击!更可怕的是,除了热点爆料会导致海量、合法的用户访问请求,还有隐藏在暗处的黑客们源源不断发起的各种 DDoS/HTTP Flood 攻击,这些都会分分钟让你的网站瞬间瘫痪。无论是合法的,还是非法的,这些流量就像一支庞然大军,具有摧枯拉朽的绝对实力。
因此,在互联网+追求合作共赢的时代,热门网站更加需要专业团队的安全保障。安全防护类产品可在网站前面砌起一座城墙,将流量大军挡在城门之外,同时将被访问网站的真实服务器隐藏起来,只接受少量、合法的流量的请求访问。专业团队固然能够提供 DDoS/CC/WAF 等各种防护手段来过滤异常流量,但这不是我们今天的主题,我们要说的,是在防护的同时对网站的优化手段:CDN 缓存优化。
首先让我们来看一张 CDN 的整体架构图:
我们知道,对网站请求返回的文件,有动态文件和静态文件两类。所谓静态文件,具有一个特点,有点类似数学中“幂等”的概念。请求静态文件好比掷骰子,每次都能掷到六点。而请求动态文件,则不然,这次掷到六点,下次可能是五点,也可能是一点。所以,对于静态文件,只要在 Web Server 上不更新,在响应同样的请求时(主要是 HTTP GET 请求),不管请求多少次,都会返回一样的文件内容。动态文件则反之,对于每个请求(HTTP GET/POST),都可能会返回不一样的文件内容。Web Server 返回的图片,文档,点播视频,一般都是静态文件,而像 html,php,jsp 文件,一般都是动态文件。
静态文件都可以被 CDN 系统缓存下来,甚至某些动态文件,也可以在很短时间(比如 1 秒内)将其看成是静态的,从而缓存下来。一旦这些文件被缓存到 CDN 系统中,那流量大军期望通过攻击这些文件从而达到使网站宕机的目的,难度将大大提升,甚至毫无希望!
我们知道,CDN 系统是分布式的。就拿云防护来说,我们在全国各地都部署了缓存节点,每个节点都有同一份文件的拷贝,流量通过智能 DNS 系统调度,用户会优先访问距离自己最近的缓存文件,从而得到想获取的内容。这样,从全国各地发起集中涌现到源站的突增流量,就被分散到了各地的分布式节点上。在文件过期之前,Web Server 甚至不会感知到这些访问请求。这就好比,静态文件真身坐镇大本营,各分身负责接待这些流量小分队。当分身能正常分发给访客时,其真身可以高枕无忧。
但这并不是万无一失的完美解决方案,我们还需要考虑到缓存文件会过期的可能性。这就好比,我们的真身在一段时间后(从几分钟到几天不等),任期结束了,换了一位新的继任者。此时,CDN 缓存节点需要重新向 Web Server 重新发起请求,获取新文件,生成新的分身。另外,当网站刚接入 CDN 缓存系统时,也是需要向 Web Server 发起请求拷贝文件的。此时此刻,如果有流量部队刚好“造访”,CDN 缓存节点没法直接响应,只能将流量导向 Web Server,在这短暂的瞬间,Web Server 中的真身会被暴露,其可能会被打垮!
如果缓存过期了,就真的无计可施了吗?
当然不是!
云防护 CDN 系统早已考虑到了这种情况,并有至少三种解决对策:
(1)通过建立缓存父层将回源流量进行收敛。
我们把 CDN 缓存节点向 Web Server 发起的请求称为回源请求,将 Web Server 称为源站。当静态文件过期时,缓存节点不直接回源,而是先请求上一级的缓存节点,由上一级的缓存节点回源。这是一个树型结构,父层节点在顶层。大量缓存节点在叶子处,它们访问父层,再由少量的父层节点回源。通过这样的层级收敛,将大量的缓存节点请求,转化成少量的父节点回源请求,从而大大减小源站的压力。
(2)缓存节点至父层合并回源。
大量的缓存节点向父层(或源站)发起请求时,也势必会给父层节点造成很大的压力。针对这种情况,云防护有自研的解决方案,可将同一个文件的大量并发请求,合并成一个或者少量请求返回给父层,从而使父层的请求数大大减少,缓解父层压力。而这种技术,父缓存节点是无感知的。我们将这个技术称之为“合并回源”。目前 Squid/Apache Traffic Server 等开源软件,都采用了这种技术。
(3)条件回源。
当缓存节点发现文件过期时,常规做法是重新下载整个文件,覆盖旧文件。在文件体积比较小的情况下,这种方式问题不大;可一旦文件体积过大,大量的文件更新,对服务器也是一种考验。此时,我们会启用条件回源:当缓存节点认为缓存文件过期时,源站文件可能并未真正更新,只是到了例行的过期检查时间点而已。此时,我们会在 HTTP 头部携带特定的信息,来询问源站是否真的更新了文件。事实经验告诉我们,在大部分情况下,源站文件并没有真正更新。通过这种方法我们可以避免粗暴无用的重复下载整个文件。
这样看来,在大部分情况下你都无需担心了。但还有这样一种情况可能发生,那就是 Web Server 由于种种原因,无法提供正常服务了(比如由于自身原因,出现短暂宕机)!而此时静态文件又恰好过期,这样一来,缓存节点的流量会导向源站,源站又不能正常提供服务,在访客看来,网站就无法访问了。
这可怎么办呢?!
不用怕!
云防护系统也同样考虑到了这种情况,并至少提供以下三种解决方案:
(1)使用旧缓存文件
当缓存节点回源时,源站有故障,会返回异常页面,如 502/504,或超时等。此时,缓存节点直接使用旧文件响应访客。在访客看来,能正常访问到内容,并不会感知到源站出现故障。此外,我们还会对旧文件强制延长一个短的过期时间,防止短时间内出现频繁回源的情况。
(2)永久在线
对于开启此功能的网站,云防护会通过自研的爬虫程序,定期抓取客户源站的内容,缓存到我们的数据服务器上,我们称之为“镜像”。当源站出现问题时,缓存节点也可以将访问请求导向“镜像”文件,从而正常响应数据给访客。
(3)重保只读
和永久在线原理类似,重保只读也是通过爬虫生成“镜像”文件。只是,此时“镜像”可以充当源站使用(伪源站),源站在重保只读期间,可完全关闭。所有的请求或攻击,都不会导向真正的源站,这样即可保证重要时期源站不会因攻击或访问量过大而出现问题。
当然,以上策略都是可能有损的。前面讲过,对动态文件的请求是非幂等的,即使强制缓存起来,也没有大太作用。另外像 SNS 网站有较强的交互性,除了下载以外,还会上传文件(HTTP POST/PUT)。这些数据,目前都没有办法用以上策略解决。当 Web Server 出现问题时,还需要网站尽快针对自身问题从根源上进行修复。
至此,CDN 缓存系统和 Web Server 各司其职,相得益彰。你不知道的是,云防护 CDN 缓存内部其实还有许多其他方面的优化:
(1)Range 请求的缓存及回源合并。
(2)热点文件分级缓存。
(3)全链路 IPv6 的支持。
(4)全链路 HTTP2 等协议的支持。
(5)基于日志的分析与监控。
(6)基于监控的动态父层设计。
所有这些,都是为了一个共同的目标:最大程度的为客户网站提供防护!接入云防护,安全无忧~