作为年轻人的潮流聚集地,Qzone在每个特殊的日子总会迎来一波猛烈的流量冲击。比如刚过去的520,下图是今年5月20号的流量情况:
可以看到,0点的时候有一大波流量,13点的时候又有一波。
尽管在前端做足了柔性策略,比如充分利用客户端缓存、高峰期限制刷新频率、高负载时直接返回304等,依然会有大量的请求到达后端。
而服务器因为CPU、内存、带宽等原因,处理能力总是有限的,当超过服务器处理能力的请求到达,所有请求都在等待处理,后续的请求又源源不断的堆积过来,最终可能会造成雪崩。这就需要后端做好过载保护,不然只能出大招了:重启!
对于web服务的负载保护,常用的做法是在web服务前面加一层反向代理,比如使用nginx作为接入层来分发请求给node进行处理,然后可以使用nginx内置的负载保护模块limit_conn和limit_req来控制到达node的并发量和请求频率。
TGW:全称Tencent GateWay,是一套实现多网统一接入、外网网络请求转发、支持自动负载均衡的系统。
不过这种方案有些不足的地方:首先多了一层服务增加了系统复杂度,另外在配置nginx的时候需要清楚各个node服务器的负载能力,一般会根据压力测试来得到具体的数据。但是对于不同的请求和不同的服务器,这个数据不是静态的,可能node服务器还有空闲资源,但是nginx没有分发更多的请求过来,服务器的能力没有得到充分的利用。
在Qzone的业务我们是直接使用node作为接入层,避免了上面的问题。
不过没有nginx在前面顶着,所有的压力都传导到node了。
(node说:没关系,这首锅我自己来
)
为了充分利用服务器多核CPU的运算能力,我们使用了node的cluster模块来实现多进程架构,主进程master根据CPU核数来创建相应数量的子进程worker,并负责接收请求,再分发给各个子进程进行处理。
为了实现过载保护,我们可以在master分发请求的时候做点手脚,如果当前服务器处理能力已经到达极限。那么就直接随机丢弃部分请求,丢弃数量和服务器负载情况成正相关。Node默认使用round robin算法来分发请求,具体实现可以查看源码round_robin_handle.js,现在我们hack一下这个模块,在分发前先判断服务器的负载情况。
在web服务的场景,CPU是主要的瓶颈,所以这里使用CPU利用率作为负载的指标。在CPU利用率达到85%的时候开始随机丢弃部分应付不过来的请求,在接近100%的时候几乎是全量丢弃,避免了服务器雪崩。
为了更充分利用服务器的处理能力,让丢弃的请求数量更平滑,这里丢弃的比率函数选择y=x^1.5,如下图最右。
要注意的是round_robin_handle.js属于internal内部模块,默认情况是不允许hack的,需要我们在启动的时候加上expose_internals参数,类似这样。
node --expose_internals index.js
最后来看下实际的运行效果,这是某次请求突增的监控记录,上边是服务器负载情况,下边是丢弃的请求数量。服务器稳稳地扛住了这波请求暴涨!
再看看5月20号当天的详细监控,这是通过TSW管理端直接看到的情况。
(AI有话说:这些中二的人类,20185201314 不就是10010110011001000011011101010100010 吗,有什么特别的…)
最新开源的TSW框架(tswjs.org)已经集成过载保护能力,自从用了TSW,妈妈再也不用担心我过年过节要重启服务器了。