基础
超时控制是一个非常简单的东西,它是指在规定的时间内完成操作,如果不能完成,那么就返回一个超时响应。
超时控制目标
一是确保客户端能在预期的时间内拿到响应。
二是及时释放资源。这其中影响最大的是线程和连接两种资源。
超时控制形态
超时控制从形态上来看分成两种。
- 调用超时控制,比如说你在调用下游接口的时候,为这一次调用设置一个超时时间。
- 链路超时控制,是指整条调用链路被一个超时时间控制。比如说你的业务有一条链路是 A 调用 B,B 调用 C。如果链路超时时间是 1s,首先 A 调用 B 的超时时间是 1s,如果 B 收到请求的时候已经过去了 200ms,那么 B 调用 C 的超时时间就不能超过 800ms。
确定超时时间
- 根据用户体验来确定
- 根据被调用接口的响应时间来确定
- 根据压测结果来确定
- 根据代码来确定
监听超时时间
框架客户端监听超时时间的情况下,如果在发起请求之前,就已经超时了,那么框架客户端根本不会发起请求,而是直接返回超时响应。这等于直接帮我们中断了业务的后续步骤。
而框架服务端监听超时的情况下,如果在收到请求的时候就已经超时了,那么框架服务端根本不会调用服务端应用代码,而是直接给框架客户端返回一个超时响应。
正常来说,对任何第三方的调用我都会设置超时时间。如果没有设置超时时间或者超时时间过长,都可能引起资源泄露。比如说早期我们公司就出现过一个事故,某个同事的数据库查询超时时间设置得过长,在数据库性能出现抖动的时候,客户端的所有查询都被长时间阻塞,导致连接池中的连接耗尽。
亮点
链路超时控制
链路超时控制和普通超时控制最大的区别是链路超时控制会作用于整条链路上的任何一环。例如在 A 调用 B,B 调用 C 的链路中,如果 A 设置了超时时间 1s,那么 A 调用 B 不能超过 1s。然后当 B 收到请求之后,如果已经过去了 200ms,那么 B 调用 C 的超时时间就不能超过 800ms。因此链路超时的关键是在链路中传递超时时间。
怎么传递超时时间,关键词是协议头。
大部分情况下,链路超时时间在网络中传递是放在协议头的。如果是 RPC 协议,那么就放在 RPC 协议头,比如说 Dubbo 的头部;如果是 HTTP 那么就是放在 HTTP 头部。比较特殊的是 gRPC 这种基于 HTTP 的 RPC 协议,它是利用 HTTP 头部作为 RPC 的头部,所以也是放在 HTTP 头部的。至于放的是什么东西,就取决于不同的协议是如何设计的了。
正常来说,在链路中传递的可以是剩余超时时间,也可以是超时时间戳。
目前来说剩余超时时间用得比较多,一般是以毫秒作为单位传递一个数值。它的缺点是服务端收到请求之后需要减去网络传输时间,得到真正的超时时间。
而超时时间戳则涉及到时钟同步的问题,不过大多数情况下时钟之间的差值都很小,和超时时间动辄几百毫秒比起来,不值一提。
一般超时时间传递的就两种:剩余超时时间或者超时时间戳。比如说剩余 1s,那么就用毫秒作为单位,数值是 1000。这种做法的缺陷就是服务端收到请求之后,要减去请求在网络中传输的时间。比如说 C 收到请求,剩余超时时间是 500ms,如果它知道 B 到 C 之间请求传输要花去 10ms,那么 C 应该用 500ms 减去 10 ms 作为真实的剩余超时时间。不过现实中比较难知道网络传输花了 10ms 这件事。
而传递超时时间戳,那么就会受到时钟同步影响。假如说此时此刻,A 的时钟是 00:00:00,而 B 的时钟是 00:00:01,也就是 A 的时钟比 B 的时钟慢了一秒。那么如果 A 传递的超时时间戳是 00:00:01,那么 B 一收到请求,就会认为这个请求已经超时了。 当然,正常来说时钟同步不至于出现那么大的偏差,大多数时钟偏差几乎可以忽略不计。不过在时钟回拨的场景下,还是会有问题。我之前听说不同云服务商之间的时钟同步问题比较严重,可能也需要注意。
怎么计算请求的网络传输时间。你就可以这样回答:
计算网络传输时间最好的方式就是使用性能测试。在模拟线上环境的情况下,让客户端发送平均大小的请求到服务端,采集传输时间,取一个平均值作为网络传输时间。另外一个方式就是不管。比如说正常情况下,A 调用 B,A 和 B 都在同一个机房,网络传输连 1ms 都不用。相比我们超时时间动辄设置为几百毫秒,这一点时间完全可以忽略不计。不过万一服务涉及到了跨机房,尤其是那种机房在两个城市的,城市还离得远的,这部分时间就要计算在内。
性能测试一定要尽可能模拟线上环境,尤其是线上环境可能会有更加复杂的网关和防火墙设置,这部分也会影响传输速率。
此文章为9月Day27学习笔记,内容来源于极客时间《后端工程师的高阶面经》