Skip to content

由于网络问题、系统或者服务内部的 Bug、服务器宕机等问题的不确定性,我们的系统或者服务永远不可能保证时刻都是可用的状态。

需要用到的 超时(Timeout)重试(Retry) 机制减小影响。

超时机制

什么是超时机制?

超时机制:一个请求超过指定的时间还没有被处理的话,直接被取消并抛出指定的异常或者错误(比如 504)。

超时主要分为:

  • 连接超时(ConnectTimeout):客户端与服务端建立连接的最长等待时间。
  • 读取超时(ReadTimeout):客户端和服务端已经建立连接,客户端等待服务端处理完请求的最长时间。更重要

一些连接池客户端框架中可能还会有获取连接超时和空闲连接清理超时。

如果没有设置超时的话,就可能会导致服务端连接数爆炸和大量请求堆积的问题。

这些堆积的连接和请求会消耗系统资源,影响新收到的请求的处理。严重的情况下,甚至会拖垮整个系统或者服务。

超时时间应该如何设置?

超时值设置太高或者太低都有风险。

  • 如果设置太高的话,会降低超时机制的有效性,比如你设置超时为 10s 的话,那设置超时就没啥意义了,系统依然可能会出现大量慢请求堆积的问题。
  • 如果设置太低的话,就可能会导致在系统或者服务在某些处理请求速度变慢的情况下(比如请求突然增多),大量请求重试(超时通常会结合重试)继续加重系统或者服务的压力,进而导致整个系统或者服务被拖垮的问题。

建议在 1000ms ~ 5000ms (1500ms 最优),通常将超时的值放在配置中心中,更佳灵活。

重试机制

重试机制一般配合超时机制一起使用,指的是多次发送相同的请求来避免瞬态故障和偶然性故障。

常见的重试策略有哪些?

常见的重试策略有两种:

  1. 固定间隔时间重试:每次重试之间都使用相同的时间间隔,比如每隔 1.5 秒进行一次重试。但是不是很好决定间隔时间。

    1. 适用于比较稳定可预测的场景。
  2. 梯度间隔重试:根据重试次数的增加去延长下次重试时间,比如第一次重试间隔为 1 秒,第二次为 2 秒,第三次为 4 秒,以此类推。重试成功的几率会更高。相对复杂,需要记录请求状态。

    1. 适用于不可预测如网络波动

重试的次数通常建议设为 3 次,并使用梯度间隔重试策略,太多次重试会对系统负载造成比较大的压力。

什么是重试幂等?

超时和重试机制在实际项目中使用的话,需要注意保证同一个请求没有被多次执行

什么情况下会出现一个请求被多次执行呢?客户端等待服务端完成请求完成超时但此时服务端已经执行了请求,只是由于短暂的网络波动导致响应在发送给客户端的过程中延迟了。

举个例子:用户支付购买某个课程,结果用户支付的请求由于重试的问题导致用户购买同一门课程支付了两次。对于这种情况,我们在执行用户购买课程的请求的时候需要判断一下用户是否已经购买过。这样的话,就不会因为重试的问题导致重复购买了。

Java 中如何实现重试?

  • 可以手动循环实现。一般不建议
  • 第三方开源库提供了更完善的重试机制实现,例如 Spring Retry、Resilience4j、Guava Retrying。

正在精进