头图

我们是如何解决偶发性的502错误的

从ingress的监控中心,我们看到,失败率虽然不高,但始终保持在0.05到0.1的水平:

image.png

我们用这样的条件去查询,发现绝大部分错误是502错误:

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

image.png

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

502 Bad Gateway是指错误网关,无效网关;在互联网中表示一种网络错误。 表现在WEB浏览器中给出的页面反馈。 它通常并不意味着上游服务器已关闭(无响应网关/代理) ,而是上游服务器和网关/代理使用不一致的协议交换数据。 鉴于互联网协议是相当清楚的,它往往意味着一个或两个机器已不正确或不完全编程。

还有人说是超时导致的:

image.png

马上在评论区有人反驳:

image.png

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

504错误代表网关超时(Gateway timeout),是指服务器作为网关或代理,但是没有及时从上游服务器收到请求。 服务器(不一定是Web 服务器)正在作为一个网关或代理来完成客户(如您的浏览器或我们的CheckUpDown 机器人)访问所需网址的请求。

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

而且从我们对502错误日志的进一步分析来看,发生502错误时的请求时间和响应时间都极短,不可能是超时。

image.png

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

image.png

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

为此我们把nginx的logtail日志的stderr输出打开:

image.png

此前这里本来是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设置的地方,给它增加一个新的属性:

image.png

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

image.png

原先不断产生的502错误彻底消失了!

再来看一下错误图:

image.png

注意那个黄颜色的5XX比例,从我们设置好的那一瞬间,永远趴在了地上!


日新亭
苟日新,日日新,又日新。

现任北京联云天下科技有限公司技术副总裁。1994年毕业于清华大学计算机科学与技术专业;20多年软件开发...

13.4k 声望
4.7k 粉丝
0 条评论
推荐阅读
只在工作日执行的脚本
但是工作日有时候又不是简单的周一到周五,比如赶上个小长假什么的,有时候周六要上班,有时候周一又不上班,这时候这样的设置就容易乱,那我们怎么设置这个cronjob才能让他预知我们的放假排班计划,并做到丝毫不...

张京7阅读 1.7k评论 2

封面图
花了几个月时间把 MySQL 重新巩固了一遍,梳理了一篇几万字 “超硬核” 的保姆式学习教程!(持续更新中~)
MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。

民工哥11阅读 1.2k

封面图
又一款内存数据库横空出世,比 Redis 更强,性能直接飙升一倍!杀疯了
KeyDB是Redis的高性能分支,专注于多线程,内存效率和高吞吐量。除了多线程之外,KeyDB还具有仅在Redis Enterprise中可用的功能,例如Active Replication,FLASH存储支持以及一些根本不可用的功能,例如直接备份...

民工哥4阅读 949评论 1

封面图
Win10 安装Docker以及Jenkins(超级详细篇)
安装Docker下载地址:[链接] ,按照它的指引教程,无脑下一步即可。安装成功后电脑会重启。打开docker桌面端,会显示进入链接,下载WSL 安装包进行无脑安装即可。安装 Linux 内核更新包 (重启电脑)重启 Docker ...

九旬3阅读 499

http 和 https 的通信过程及区别
🎈 两者的区别端口: http 端口号是80, https 端口号是443传输协议: http 是超文本传输协议,属于明文传输; https 是安全的超文本传输协议,是经过 SSL 加密后的传输协议安全性: https 使用了 TLS/SSL 加密,...

tiny极客2阅读 2.8k评论 2

封面图
100 行 shell 写个 Docker
在初接触Docker的时候,我们必须要了解的几个概念就是Cgroup、Namespace、RootFs,如果本身对虚拟化的发展没有深入的了解,那么很难对这几个概念有深入的理解,本文的目的就是通过在操作系统中以交互式的方式去理...

vivo互联网技术2阅读 453

openSSH升级公钥失效Permission denied (publickey)解决方案
背景centos系统升级openSSH后,服务器上无法再拉取代码,报错如下:原因服务器重新安装SSH后公钥改变,需要重新安装ssh-keygen -t rsa -C "邮箱地址"生成公钥,删除gitlab原有公钥,但是git pull依然报错如下:依...

兰俊秋雨2阅读 1.5k

现任北京联云天下科技有限公司技术副总裁。1994年毕业于清华大学计算机科学与技术专业;20多年软件开发...

13.4k 声望
4.7k 粉丝
宣传栏