聊聊RPC的超时时间

聊聊RPC的超时时间

开篇

这个故事的起因是我在和中间件的同事们聊天的时候聊到的。关于超时时间,目前大部分的做法是按照最大正常相应时间设置,而并非一个TP995得出来的平均响应时间之类的。举个例子:

这是我在我们这边的监控系统上随机抓了个请求的响应时间分布,大体上是这样的,某个时间段内:4561次请求,4489次的响应时间在30ms之内,69次在200ms之内,3次在1000ms之内。

我们现在通常的做法是设置一个1000ms的超时时间。为什么,因为毕竟那三次都是正常的请求,只是因为一些特殊原因导致响应时间比较久(比如:获取某个用户的歌单的时候,某几个用户关注了几万个歌单,那么查询时间也好,数据传输的时间也好必然慢)。如果我们把超时时间设置为200ms,那么:对于这些正常请求,必然超时。

问题

目前大部分情况下,我们这么看来好像并没有什么问题。因为大部分情况下,99%的请求确实会成功,1%的请求确实需要1000ms才能正常返回。

但是我们程序员要做的事情之一就是——预防小概率。我们总要防备那些特定的情况出现:要是数据库抖动啦,网络抖动啦,机房抖动啦,地震啦,电缆被挖断啦之类的。

我再举一个稍微明显的例子。我们大部分正常请求是1s超时,极少部分正常请求5s超时,这时候按照我们之前说的,我们把超时时间设置为5s。然后看似一切都很正常的运行在线上,直到有一天:数据库抖动,抖得非常厉害,以至于我们的请求响应时间全部都飙升到4.99s。这时候,我们设置的超时时间是5s,所以,系统不会报警,我们以为一切正常,惨的是底层那些服务器——本来我处理的请求,99%的是只用1s就可以了,突然,99%的请求都需要处理4.99s了,底层服务器可能已经垮掉了,因为它本身的容忍能力、服务能力根本没有这么强大!

但是就是由于这个5s的超时,我们还在吃着火锅唱着歌,以为一切都正常,熔断器也在吃着火锅唱着歌,以为底层的服务一切安好~

问题暴露出来了,超时时间过大——不准确!!!我们对于底层服务的变动——不敏感!!!

解决思路

怎么办?这个时候,我们这边的RPC框架的一个特性可以很好地应付这个问题:快车道 + 慢车道

什么意思,我给大家解释一下。跟我们平时走高速公路一个道理,大部分的车都很快,100公里/h。但是就是有些车,他们由于种种原因,就是跑不快(那些满请求)。这时候我们的高速公路怎么办呢?

给他们单独设置一个慢车道

目前大部分框架都有重试机制,以Dubbo为例子,即为超时重试(或者服务端断连时)。目前默认会重试三次,即:第一次超时了,换个服务器再调用一次。

那我们解决上面问题的思路可不可以这样:我们设置两个超时时间,第一个超时时间timeout=1s,第二个超时时间slowTimeout=5s

假设配置好了,有一个满请求过来了,是正常请求但是需要用耗时4.9s。我们以三次重试为例,第一次超时了,换第二个试试,第二个也失败了,这时候!我们使用slowTimeout作为超时时间试试,结果成功,ok!我们就把结果返回给用户。

熔断怎么办

可能有同学要问了,那你什么时候熔断呢?

我们这边的熔断,是C端即客户端熔断。判断依据是,比如,某个时间段内,超时请求超过20%熔断。那我们还说刚才的例子,第一次失败,第二次失败,第三次成功了,我们怎么记录呢?

答案是记录第一次第二次超时。

这样记录会不会导致超时请求很多呢?相比于不记录超时呢(毕竟虽然重试了两次但是最终成功了)?

第一个问题:不会很多,也不会错误的触发熔断。因为我们说过了,99%的请求,我们是按照1s的超时处理的,只有1%的请求才会按照5s超时来处理,假设一切正常,只有1%的请求会触发重试两次第三次成功,所以,不会很多,也不会错误的触发熔断。

第二个问题:如果最终成功就不记录超时时间,则会导致实际超时时间会以slowTimeout为准,这是错误的。因为如果服务器抖动,大量请求全部超时,响应时间都为4.9s了,这时候,最后一次以5s超时的情况下仍然会返回成功。但是其实99%的请求都超时了,如果记录为一次正常请求,则会出现之前说过的不敏感问题。

其他解决思路

其实这种问题还有其他的解决方案,比如动态调配、服务分组等。

动态调配:动态的划分请求。以上述为例,某些用户的请求,我直接路由到一个特殊的集群上,集群的超时时间全都是5s。这样也可以解决上述问题。但是麻烦的点,一是实现动态调配比较麻烦,二是调配之后,加入某个用户一开始关注了1000个歌单,后来突然全都取消关注,这时候他的请求响应时间又会降低到一个很低的水平,这个时候需要我们再次调配回去,否则又会出现不敏感问题。如果全自动化调配,那需要数据分析介入,就更加复杂了。

好处也不是没有,我们可以发现,这种动态调配更加准确。比如我们正常请求85%的响应时间1s,10%在3s,5%在5s,如果我们可以动态调配,完全可以分配不同的资源,不同的超时时间等等来区分对待这三种正常请求。

服务分组:思想类似,把服务分组,有些组服务器性能非常好,处理这些响应时间长,处理时间长的请求。

后记

这里我们只是抛砖引玉的那个砖头,还是希望大家多多思考实际运用中的效果以及场景。