一次新品发布会就使网站首页十分钟之内迎来了三万次的点击量,图片开始旋转、接口响应时间由原来的200毫秒延长至4秒,并且数据库连接池也被挤爆了,整个页面都变白了。事后分析得知,其实服务器的带宽只占用了30%左右,CPU也没有达到饱和状态,“每次访问都要重新计算”才是真正导致问题的原因所在。

并发压力的方向
企业官网的流量构成其实是非常固定的:HTML文档、CSS/JS、图片和字体,再加上一些少量的接口调用。前三者占了全部请求的85%以上,并且每隔十分钟的内容都一模一样。这就意味着大部分并发请求并不需要服务器进行任何计算就可以得到结果了。因此提高网站并发能力的第一个步骤并不是增加服务器数量,而是尽可能多地生成不需要经过服务器处理即可直接响应的请求。
浏览器缓存使第二次及以后的访问费用几乎为零
HTTP响应头决定了所有的事情,对于不会经常变化的文件如css、js、图片、字体等就使用long-cache:
Cache-Control: public, max-age=31536000, immutable
immutable会让浏览器不再发送协商请求,尤其是对于移动设备来说效果很好;而HTML文档则正好相反,使用no-cache或者max-age=60加上ETag来做304协商缓存——浏览器带着If-None-Match去请求的时候,源站只返回一个空响应体,这样传输的数据量就从几十KB降到了几百字节。
给文件重命名要比打上时间戳更加可靠一些
继续使用 style.css?v=1.2.3 进行版本管理会带来两个问题:一部分代理服务器不会缓存带有查询参数的网址;在改版的时候新旧版本一起使用会导致样式错乱。更好的办法是在构建过程中给文件名加上内容哈希值,比如 app.4f3a91.css 这样的指纹文件,在HTML中引用它。一旦内容发生变化,哈希值就会随之改变,URL也会相应地更新从而自动失效,无需人工干预就可以把缓存时间设置为一年之久。
CDN的边缘缓存可以拦截大部分来自源站的请求
CDN就是并发能力的放大器,主要的配置点有以下几点:
TTL 分级管理制度:有哈希值的静态文件为30天~1年,图片为7天~30天,首页为5~10分钟,活动页面另行设定较短的时间。
启用stale-while-revalidate之后,在缓存失效的时候先使用老数据来响应请求,并且后台会进行异步回源操作,因此用户的体验不会受到影响。
回源使用长连接和HTTP/2来降低握手的成本。
是否生效可以通过查看响应头中的X-Cache或者Age字段来判断:如果是HIT的话就说明被边缘节点缓存住了;而如果是MISS则表示没有命中边缘节点而是直接请求到了源站。成熟的官网配置运行一段时间后,边缘命中率一般可以达到90%左右,剩下的10%就是源站需要应对的实际并发量了。
用Nginx来做源站缓存,拦截掉穿过的这部分
即使使用了CDN也会有回源请求被送到源服务器上,因此再加一层proxy_cache来过滤:
proxy_cache_path /data/cache levels=1:2 keys_zone=site_cache:20m inactive=60m max_size=2g。
proxy_cache_valid 200 302 10m
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504
proxy_cache_lock开启
后面两句才是重点。用 stale 可以让后端出现短暂故障的时候仍然能返回老数据,并且不会造成页面空白;用 cache lock 可以确保同一个资源在并发请求的情况下只能有一个请求被处理,其他的会排队等待,这样可以避免缓存穿透问题。
动态内容缓存的界限要划分清楚
可以缓的是新闻列表、商品详情、公开文档、不包含个人信息的服务接口;不可以缓的是登录状态页面、购物车、带有Set-Cookie的响应、POST请求、实时库存和价格。最容易忽视的就是Vary头,在根据User-Agent进行移动端适配的情况下,缓存键要区分UA,否则移动用户就会得到PC版的页面。另外一种高风险的情况就是Cookie穿透问题,即登录用户的Set-Cookie被边缘节点缓存之后发送给所有用户,必须在上线前加入检查清单中。
失效的方式是不要让过期的信息一直显示在用户的屏幕上
用三种方式来解决这个问题:文件名哈希来对静态资源进行缓存;CDN控制台可以根据目录或者URL主动刷新,在紧急修改的时候使用它;发布流程中加入预热接口,在上线之前把首页和重要页面推送到边缘节点上,避免发布时大量的回源导致源站崩溃。同时给TTL加一个随机抖动的效果,比如600秒左右上下波动60秒,这样就不会因为同一次更新而全部失效了。
以数据为依据,不以感觉来衡量
在Nginx日志中写入$upstream_cache_status,并计算出HIT、MISS和EXPIRED的比例;使用wrk或者ab工具对预发布环境进行压力测试,在开启缓存之前和之后分别测量QPS以及P99延迟值的变化。某制造企业官网通过增加浏览器长缓存、CDN分级TTL以及源站proxy_cache等设置后,在相同的配置下单机并发能力由原来的900提升至现在的5200,首页首屏加载时间也由原来的2.4秒缩短到现在的0.6秒,并没有增加任何硬件成本。
缓存并不是简单的把数据保存起来就完了,而是要有一套关于“哪些可以复用、多久之后还要重新计算”的判断标准。企业网站的并发处理能力主要取决于有多少请求不需要再查询源站了。先做好静态资源缓存头部配置,再进行CDN的TTL分级设置,最后用源站缓存以及锁机制作为保障,按照这样的步骤来实施的话,成本最低而且效果最好。