乐趣区

关于运维:我们是如何解决偶发性的502错误的

从 ingress 的监控核心,咱们看到,失败率尽管不高,但始终保持在 0.05 到 0.1 的程度:

咱们用这样的条件去查问,发现绝大部分谬误是 502 谬误:

status>=500 | select status, count(*) a group by status order by a desc

那么 502 谬误到底是个什么谬误呢?百度百科给出的解释是:

502 Bad Gateway 是指谬误网关,有效网关;在互联网中示意一种网络谬误。体现在 WEB 浏览器中给出的页面反馈。它通常并不意味着上游服务器已敞开(无响应网关 / 代理),而是上游服务器和网关 / 代理应用不统一的协定替换数据。鉴于互联网协议是相当分明的,它往往意味着一个或两个机器已不正确或不齐全编程。

还有人说是超时导致的:

马上在评论区有人反驳:

百度百科对 504 谬误的解释:

504 谬误代表网关超时(Gateway timeout),是指服务器作为网关或代理,然而没有及时从上游服务器收到申请。服务器(不肯定是 Web 服务器)正在作为一个网关或代理来实现客户(如您的浏览器或咱们的 CheckUpDown 机器人)拜访所需网址的申请。

显然,504 谬误才是超时,而 502 并不是。

而且从咱们对 502 谬误日志的进一步剖析来看,产生 502 谬误时的申请工夫和响应工夫都极短,不可能是超时。

查 502 与 504 的区别,只有这个说法绝对靠谱:

也就是说咱们后端的服务是可能响应的,但响应不符合要求,所以呈现了 502 谬误。但这种谬误并不是必然的,如果是必然呈现,则网站整体不可用,早就被发现了,正因为它是偶发的,所以有必要看一下在产生 502 的时候到底产生了什么。

为此咱们把 nginx 的 logtail 日志的 stderr 输入关上:

此前这里原本是 false,当初咱们把它改成 true,使它可能将谬误日志输入进去,便于咱们查找起因。

stderr 谬误输入之后,立即就能在日志里看到大量的这种谬误:

2022/04/02 16:59:55 [error] 11168#11168: *739601507 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 49.93.83.68, server: www.domain.com, request: "POST /myserver/service HTTP/1.1", upstream: "http://192.108.1.121:8080/myserver/service", host: "www.domain.com"

从字面意思来了解,是说上游服务器间接敞开了连贯。然而上游服务器为什么要敞开连贯呢?将错误信息放入搜索引擎进一步排查,很多文章将咱们的思路导向了 keepalive 这个方向,最应该查看的是 keepalive_timeout 和 keepalive_request 这两个属性。

什么是 keepalive?这是 http 1.1 协定的缺省配置,在 http 1.0 的时候,如果你的网页上有 10 个图片,那么浏览器和服务器之间要同时建设 10 个连贯,把这 10 个图片发过来而后再敞开这 10 个连贯,显然对于服务器来说,建设 10 个连贯再敞开 10 个连贯,耗费是比拟大的。所以在 http 1.1 协定里减少了 keepalive 的性能,在发 10 张图片的时候只须要建设一个连贯就够了,只有还有内容要传输,这个通道会始终保持凋谢状态,不会在传输结束之后立即敞开,这就是 keepalive 保活的意思。

然而 keepalive 不能把这个连贯永远放弃,如果没有内容了还持续放弃,无疑也是一种节约,所以这里就产生了超时的概念,keepalive_timeout 的意思是说如果这个连贯当中没有内容传输了并且超过了这个工夫,那么就把这个连贯断掉,keepalive_requests 的意思是说咱们这个连贯最多容许传输多少个内容,超过这个内容那么也把它断掉。

那么这个 keepalive_timout 和咱们的 502 谬误之间有什么关系呢?因为所有网站的架构都不是浏览器间接连贯后端的应用服务器,而肯定是两头有 nginx 服务器做反向代理的,浏览器和 nginx 服务器之间建设 keepalive 连贯,nginx 再和后端的应用服务器建设 keepalive 连贯,所以这是两种不同的 keepalive 连贯。咱们把浏览器和 nginx 之间的 keepalive 连贯叫做 ka1,把 nginx 和应用服务器之间的 keepalive 连贯叫做 ka2。

如果 ka1 的超时设置为 100 秒,也就是说如果 100 秒之内没有新内容要传输,就把 nginx 和浏览器之间的连贯断掉。而同时,咱们把 ka2 设置为 50 秒,也就是说如果 nginx 和应用服务器之间没有新内容要传输,那么就把应用服务器和 nginx 之间的连贯断掉。那么这时候就会产生一个问题:前 50 秒没有传输内容,在第 51 秒的时候,浏览器向 nginx 发了一个申请,这时候 ka1 还没有断掉,因为没有到 100 秒的工夫,所以这是没有问题的,然而当 nginx 试图向应用服务器发申请的时候就出问题了,ka2 断了!因为 ka2 的超时设置是 50 秒,这时候曾经超了,所以就断了,这时候 nginx 无奈再从应用服务器取得正确响应,只好返回浏览器 502 谬误!

然而咱们基本就没有设置过这些参数啊,怎么会有这种问题呢?

这没关系,既然没有设置过,那零碎必定用的是缺省参数,咱们来看一下 ka1 的缺省设置是多少,也就是 nginx(ingress) 和浏览器之间的缺省的 keepalive_timeout 值:

upstream-keepalive-timeout

Sets a timeout during which an idle keepalive connection to an upstream server will stay open. default: 60

ka1 的缺省设置是 60 秒。

咱们再看一下 ka2 的缺省设置是多少秒,Tomcat 官网文档上说:

The number of milliseconds this Connector will wait for another HTTP request before closing the connection. The default value is to use the value that has been set for the connectionTimeout attribute. Use a value of -1 to indicate no (i.e. infinite) timeout.

缺省值等同于 connectionTimeout 的值,那 connectionTimeout 等于多少呢?

The number of milliseconds this Connector will wait, after accepting a connection, for the request URI line to be presented. Use a value of -1 to indicate no (i.e. infinite) timeout. The default value is 60000 (i.e. 60 seconds) but note that the standard server.xml that ships with Tomcat sets this to 20000 (i.e. 20 seconds). Unless disableUploadTimeout is set to false, this timeout will also be used when reading the request body (if any).

connectionTimeout 的缺省值是 60 秒,然而,他们提供的规范的 server.xml 里把这个值设为了 20 秒!

那么当初问题就很分明了,咱们的 ka1 是 60 秒,而 ka2 是 20 秒,从 21 秒到 60 秒之间的任何工夫有申请进来都会产生 502 谬误。

找到了问题的本源,解决起来就好办了,咱们只须要确保 ka1 的超时设置小于 ka2 的设置就够了。或者批改 ka1,或者批改 ka2,都是能够的。

咱们先批改 ka1 看一下,对于 ingress 来说,要批改 ka1 须要在 ingress 的 configMap 中批改,所以咱们找到 configMap 设置的中央,给它减少一个新的属性:

这里咱们把 upstream-keepalive-timeout 设为 4,确保它低于 ka2 的 20,设置完之后,ingress 会主动加载新设置,咱们看一下后果:

原先一直产生的 502 谬误彻底隐没了!

再来看一下谬误图:

留神那个黄色彩的 5XX 比例,从咱们设置好的那一瞬间,永远趴在了地上!

退出移动版