一起因限流引发的故障

1.前言

限流是保证系统高可用性的一项很重要的举措,但是任何事情都有利弊,限流措施如果使用不当,有可能会引起比较大的问题,反而导致系统可用性降低。

2.详细经过

上周,我们负责的一个入口系统的几台服务器突然收到大量的线程池满的告警,根据以往的经验,初步判断是上游请求量激增导致的。通过监控也印证了我们的判断,因为激增的流量比较大,为保护集群,避免出现雪崩,我们开启了限流措施。本来这是保证集群可用性的一种常见措施,但因为其中某些环节做得不够细致,导致了后面故障的发生。

开启了限流措施之后,我们很快就收到了另外一个监控(搜索结果为空率)的告警。此监控是反映系统返回结果为空的比例的,当这个比例超过设定的阈值就会告警。当时这个告警飙升到了40%多,这个比例是非常恐怖的,意味着接近一半的搜索没有结果了。当时我们立刻断定是限流的影响,为了保护集群,同时不影响用户体验,我们将限流阈值调高,这时搜索为空率监控开始下降。

5分钟后,上游系统同事反馈系统请求量在下降,我们自己的监控也有所体现,为了避免伤害用户,我们关掉了限流,于是故障恢复。

3.故障分析

3.1 Hash策略不合理

事后分析原因,发现是平台上有个代理商发布了一个错误的航线价格,这个价格非常低,导致大量的用户前来搜索和购买。由于我们的集群是根据航线+日期进行hash,导致短时间内,特定的几台机器收到了大量的请求,导致机器负载过高,最终不可用。如果能够让集群的请求分布更加均匀,那么也不会导致雪崩的风险而不得不限流。

3.2 限流措施不合理

故障以前系统的限流措施是:被限流后,请求立即返回为空的结果。但这里更加合理的措施是:如果被限流,让请求在队列里面等待一会再尝试请求。这样能够达到削峰的效果,让请求更加平滑,同时也降低对用户体验的伤害。

3.3 请求合并不合理

为减轻对后端系统的压力,我们系统中保留了本地缓存,这样能够针对相同的请求使用缓存,也能提高缓存的利用率。但因为系统单次请求的结果集比较大,为降低对本地缓存的占用,我们并未将最终结果进行缓存,而是缓存了初始结果,这样就意味着在利用缓存的时候,还需要在本地经过一些计算才能返回结果,这无疑增加了系统的压力。

所以后续我们可以做进一步优化,在一个时间片内,如果存在相同的请求,如果结果还没有计算出来,就等结果计算出来再返回;如果结果已经计算出来并在缓存中有效,直接返回结果即可。这样也能进一步降低系统的负载。

3.4 限流粒度太粗

故障以前系统是对所有请求进行限流,但故障时,只是某条航线收到了大量的请求,其余航线请求是正常的。开启限流之后,虽然大量的问题航线请求被限制住了,但同时也限制了正常的用户请求。所以在限流的粒度上,我们还可以做得更加细致。当然这里有个前提,就是我们需要在第一时间知道哪条航线请求量飙升,这样才能有针对性地进行限流。

4. 总结

高可用是一个很大的话题,就其中的限流措施来说,也可以做得非常细致。这里只是针对本次故障做一个简单的总结,对此如果你有任何意见或建议,还望不吝赐教。