接口超时时间设置的学问
为什么要设置超时
首先,consumer是服务消费方,provider是服务提供方,调用关系:用户调用consumer,consumer调用provider
-
consumer调用provider,若provider性能变差,响应变慢,就会影响consumer的响应时间,极端情况若provider一直不响应,则consumer就会一直等待,这肯定是不行的,consumer应该及时给用户响应,不能让用户长时间等待;另外,若consumer响应慢,高并发时可能引发一系列问题,比如:线程耗尽、内存飙升甚至OOM导致服务瘫痪,必须设置超时,以防大量线程阻塞在慢请求上造成系统雪崩。假设一条调用链路A->B->C->D,若D性能变差、响应变慢,就会影响所有上游。因此,设置超时时间是有必要的。
接口响应慢,高并发时可能有很多问题
-
当cosumer接受provider不返回结果,但不能接受一直等待,这种场景就要设置超时时间。比如,假设consumer是核心的商品服务,provider是非核心的评论服务,当评价服务出现性能问题时,商品服务可以接受不返回评价信息,从而保证能继续对外提供服务。
除了设置超时时间,一般还会设置超时重试(重试次数需要综合考虑),为啥要超时重试?
因为很可能本次调用超时是因为网络抖动,在某些业务场景下若超时直接放弃可能有很大损失,此时可以在超时后重试避免业务损失,保证服务可用性。
超时重试机制的副作用
- 重复请求。就算接口调用超时了,但是接口的逻辑还是会执行,若重试了就会重复请求接口,需要考虑接口是否幂等。
- 影响consumer。重试意味着consumer响应会变慢,若是高QPS接口可能会引发一系列问题。
- 爆炸式的重试风暴。假如一条调用链路经过了4个服务(A->B->C->D),最底层的服务D出现超时,这样上游服务都将发起重试,假设重试次数都设置的3次,那么B将面临正常情况下3倍的负载量,C是9倍,D是27倍,整个服务集群可能因此雪崩。
超时的实现原理
接口调用封装成异步任务,然后主线程等待异步任务完成,若等待的时候超时了,则抛出Timeout异常。
如何合理设置超时时间
超时时间和重试次数的设置,需要考虑到调用链中所有依赖服务的耗时,总之,需要全局考虑,既要考虑上游,也要考虑下游。
- 设置调用方的超时时间之前,先了解清楚依赖服务的TP99响应时间是多少(如果依赖服务性能波动大,也可以看TP95),调用方的超时时间可以在此基础上加50%
- 若接口非幂等,则不应该超时重试
- 如果从业务角度来看,服务可用性要求不用那么高(比如偏内部的应用系统),则可以不用设置超时重试次数,直接人工重试即可,这样能减少接口实现的复杂度,反而更利于后期维护
- 重试次数越大,服务可用性越高,但重试多了响应会变慢,高并发时可能引发一系列问题(一般2次,最多3次)