Go语言进阶(十九)微服务可靠性-超时

微服务可靠性-超时

超时控制

超时控制,我们的组件能够快速失效(fail fast),因为我们不希望等到断开的实例直到超时。没有什么比挂起的请求和无响应的界面更令人失望。这不仅浪费资源,而且还会让用户体验变得更差。我们的服务是互相调用的,所以在这些延迟叠加前,应该特别注意防止那些超时的操作。

  • 网络传递具有不确定性。
  • 客户端和服务端不一致的超时策略导致资源浪费。
  • ”默认值”策略。
  • 高延迟服务导致 client 浪费资源等待,使用超时传递: 进程间传递 + 跨进程传递。

超时控制是微服务可用性的第一道关,良好的超时策略,可以尽可能让服务不堆积请求,尽快清空高延迟的请求,释放 Goroutine。

image-20211227150030064

实际业务开发中,我们依赖的微服务的超时策略并不清楚,或者随着业务迭代耗时超生了变化,意外的导致依赖者出现了超时。

  • 服务提供者定义好 latency SLO,更新到 gRPC Proto 定义中,服务后续迭代,都应保证 SLO。(避免出现意外的默认超时策略,或者意外的配置超时策略。)
  • kit 基础库兜底默认超时,比如 100ms,进行配置防御保护,避免出现类似 60s 之类的超大超时策略。
  • 配置中心公共模版,对于未配置的服务使用公共配置。

image-20211227150310132

ctx超时传递

超时传递: 当上游服务已经超时返回 504,但下游服务仍然在执行,会导致浪费资源做无用功。超时传递指的是把当前服务的剩余 Quota 传递到下游服务中,继承超时策略,控制请求级别的全局超时控制。

进程内超时控制

一个请求在每个阶段(网络请求)开始前,就要检查是否还有足够的剩余来处理请求,以及继承他的超时策略,使用 Go 标准库的 context.WithTimeout

image-20211227151547637

跨进程传递

  • A gRPC 请求 B,1s超时。
  • B 使用了300ms 处理请求,再转发请求 C。
  • C 配置了600ms 超时,但是实际只用了500ms。
  • 到其他的下游,发现余量不足,取消传递。

在需要强制执行时,下游的服务可以覆盖上游的超时传递和配额。

在 gRPC 框架中,会依赖 gRPC Metadata Exchange,基于 HTTP2 的 Headers 传递 grpc-timeout 字段,自动传递到下游,构建带 timeout 的 context。

image-20211227153726711

双峰分布

  • 95%的请求耗时在100ms内,5%的请求可能永远不会完成(长超时)。
  • 对于监控不要只看 mean,可以看看耗时分布统计,比如 95th,99th
  • 设置合理的超时,拒绝超长请求,或者当Server 不可用要主动失败。

超时决定着服务线程耗尽。

image-20211227154504958

Case Study

  • SLB 入口 Nginx 没配置超时导致连锁故障。
  • 服务依赖的 DB 连接池漏配超时,导致请求阻塞,最终服务集体 OOM。
  • 下游服务发版耗时增加,而上游服务配置超时过短,导致上游请求失败。