What
本篇应该是稳定性「三十六计」系列的一篇:超时重试。但是「设置默认的超时和重试是一个基础设施的基本素养」这句话我在我们组内三次开会的时候都说了。表达了我的一个理念。
Why
为什么一个基础设施要设置默认的超时和重试?想象下面一个场景。
TCP协议里有一些基本的概念:MSL、TTL、RTT。
MSL(Maximum Segment Lifetime)
报文最大生存时间,它是任何报文在网络上存在的最长时间。超时报文就被丢弃。
TTL(Time To Live)
生存时间,是由源主机设置初始值,经常存的是ip数据包可以经过的最大路由数,每经过一个处理路由数减1,当值为0则报文被丢弃。
RTT(Round-trip time)
客户端到服务器往返所花时间。
嗯?不是很明白?这就对了。想象这些东西都没有默认值,需要我们自己去设置,是不是很头大?
作为基础设施,自己应该是做过数据统计的、做过压测的。自己的性能是什么样的,怎么使用更为合理,基础设施的开发团队最为清楚,不应该将这种设置的责任交给调用方或者客户端来做,加大对基础设施的学习成本。
再来讨论一个问题:为什么要超时和重试?
长尾问题
如上图,随便找了一个调用的耗时。从上面可以看到平均耗时13.9ms,百分之99的耗时在30ms内,最大耗时有488ms。那对于超过100ms的请求来说,是不是在30ms内还不返回就直接丢弃这个请求重新发起一个,新请求有99%的概率在30ms内返回结果,从时间上更划算?
而之所以对同一个请求性能差距很大,原因很多,常见的有服务过载和队列过长。
死锁问题
想象一个分布式锁,没有超时时间。万一释放锁时失败了,其他人永远不能获取这个锁。而如果有超时时间,锁过期后,其他的请求通过重试是可以获取到锁的。
How
怎么设置超时和重试。guava-trying是个不错的java实现。其实不管什么语言都不是难事,难的是超时和重试条件是什么,设置多少合理。
超时和重试条件根据业务不同有差异。
一般的超时条件可设置为TP95(95%的请求)的2倍。或者智能一点,根据大数据统计一下,用个机器学习算法看看怎么设置总体耗时最小。
重试的一个比较好的实践是每次重试的时间间隔成指数级增长,并且根据集群情况设置合理上限。这样就避免本来服务已经过载了,短时间内大量重试造成多米诺骨牌效应(雪崩)。
合理的上限相当重要,因为有些请求不应该再重试了。比如服务器过载时限流策略收到请求不会将请求交给执行层而是直接返回一个响应。而如果客户端判断:“我去,这么快就给我结果啦!太好了,下个请求继续给力哦~~”这种错误的正反馈循坏可能会成为资源耗尽的最后一根稻草。
相关阅读