Nginx 实战系列之四:upstream 的 max_fails 和 fail_timeout 指标和实战经验
upstream 的 max_fails 和 fail_timeout 的功能详解
功能描述和详细分析
Nginx 的 upstream 模块会实现所谓的被动健康检查,也就是利用 max_fails 机制来实现,如果请求后端 upstream peer出现一些错误,当错误的累计次数达到 max_fails,那么该 upstream peer 会被 Nginx 摘掉 fail_timeout 时间,在这个时间内,这个 upstream peer 节点禁止对外提供服务。
需要重点注意的是 fails 是一个区间内失败的累加值,也就是在 fail_timeout 的这个时间区间内,两个错误之间即使有成功的请求,fails 也依然会进行累加计算。更为精确的表述这个功能细节如下:
- • 如果在 T0 时刻出现了一个错误,那么 fails = 1;
- • 在 T0 ~ T0 + fail_timeout 的时间区间内:
- • 如果没有出现错误,那么 fails 会重置为 0;
- • 如果出现了一个新错误,比如在 T1 时刻出现一个新的错误,那么 fails 会继续累加,fails = 2
- • 接着会重新以新的时间区间 T1 ~ T1 + fail_timeout 开始统计但是 fails 值却是继续累加,如果这个时间区间又有一个新的错误,那么 fails = 3
- • 直到 T2 时刻,出现了新的错误并 fails >= max_fails,那么 peer 节点会被摘除,在 T2 ~ T2 + fail_timeout 这个时间内,节点就无法对外提供服务,并且重置 fails 为 0,然后开启新的一轮检测
曾经遇到过的线上问题
层级我们线上的通用配置是参考官方的示例配置max_fails=3 fail_timeout=30s;
,这个配置表示只要 30s 内出现了 3 次错误,那么就会被摘除 30s;这种配置在低流量下是没有明显问题的,但是我们线上的服务 QPS 都很高,至少都是万级别以上,针对这么高的 QPS ,这种配置显然不合理,因为高 QPS 的场景下,偶尔出现几个异常错误是常见的,那么这种情况下,一般可以通过其他机制来保证错误的处理。但是我们在高 QPS 的情况下, Nginx 的 upstream 是这样的配置就会使得服务出现一些毛刺,这也是我们曾经线上出现的实际问题。
经过分析讨论,fail_timeout 继续采用 Nginx 官方默认配置(注意这里是默认配置而不是他们的 sample 示例配置)的 10s,但是max_fails 需要调高,特别是对于后端 upstream 请求比较大的场景;目前我们的通用最佳实践配置是 fail_timout=10s max_fails=20
;如果 QPS 进一步增加,或者后端节点数减少,那么 max_fails 可以适当进一步调高。
当然,我们为了保证服务质量,仅仅依靠调整 fail_timout 和 max_fails 是无法保证的,因此还必须要同时引入 nginx_upstream_check_module 主动健康检查模块,这样才能保证我们服务的 SLA。
线上最终要求通用配置
fail_timout=10s max_fails=20 这个可以当做一个最佳实践
max_fails 机制 和 主动健康检查 机制需要共存的原因
max_fails 机制 和 主动健康检查 的处理逻辑如下:
- • max_fails 是访问 upstream 的所有接口请求错误都算(抛开 proxy_next_upstream 指定的一些错误类型),这样的话,当 QPS 很大的情况下,比如 3-5w QPS ;那业务访问这个 upstream,也许只在 1s 内就能达到 max_fails 次数,然后就会摘掉 fail_timeout 时间,这样就可以很快摘掉
- • 这个的问题是,只有把流量打进去,才能知道是否有异常,这个对业务是有损的。
- • 主动健康检查,是固定频率检测固定接口,比如我每隔 3s 检测一次,然后失败 2 次就认为节点不可用,但是这个最长耗时需要 6s 才能摘掉
- • check 主动检测,需要长达 6s 才能摘掉,在高并发的情况下,无法接受秒级别的失败,很多业务方会敏感,我们要去降低影响面,max_fails 相当于增加了一个熔断机制。
- • 这个的优势是提前检测,不涉及业务本身的流量;但是劣势是需要一个时间范围,不能马上摘除。
- • 共存就是为了同时工作,从不同的角度来检测健康状态,两者的优劣进行互补,刚好相互弥补缺陷,从而尽快剔除有问题的节点,提高服务质量。