解决Tengine健康检查引起的TIME_WAIT堆积问题
简介: 解决Tengine健康检查引起的TIME_WAIT堆积问题
一. 问题背景
“服务上云后,我们的TCP端口基本上都处于TIME_WAIT的状态”、“这个问题在线下机房未曾发生过” 这是客户提交问题的描述。
客户环境是自建Tengine作为7层反向代理,后端接约1.8万台NGINX。Tengine上云之后,在服务器上发现大量的TIME_WAIT状态的TCP socket;由于后端较多,潜在可能影响业务可用性。用户对比之前的经验比较担心是否可能是接入阿里云之后导致,所以希望我们对此进行详细的分析。
注:TIME_WAIT状态的监听带来的问题在于主机无法为往外部的连接请求分配动态端口。此时,可以配置net.ipv4.ip_local_port_range,增加其端口选择范围(可以考虑 5000 - 65535),但依然存在 2 MSL 时间内被用完的可能。
二. TIME_WAIT原因分析
首先,如果我们重新回顾下TCP状态机就能知道,TIME_WAIT状态的端口仅出现在主动关闭连接的一方(跟这一方是客户端或者是服务器端无关)。当TCP协议栈进行连接关闭请求时,只有【主动关闭连接方】会进入TIME_WAIT状态。而客户的顾虑也在这里。
一方面,健康检查使用 HTTP1.0 是短连接,逻辑上应该由后端NGINX服务器主动关闭连接,多数TIME_WAIT应该出现在NGINX侧。
另一方面,我们也通过抓包确认了多数连接关闭的第一个FIN请求均由后端NGINX服务器发起,理论上,Tengine服务器的socket 应该直接进入CLOSED状态而不会有这么多的TIME_WAIT 。
抓包情况如下,我们根据Tengine上是TIME_WAIT的socket端口号,进行了过滤。
虽然上面的抓包结果显示当前 Tengine 行为看起来确实很奇怪,但实际上通过分析,此类情形在逻辑上还是存在的。为了解释这个行为,我们首先应该了解:通过tcpdump抓到的网络数据包,是该数据包在该主机上收发的“结果”。尽管在抓包上看,Tengine侧看起来是【被动接收方】角色,但在操作系统中,这个socket是否属于主动关闭的决定因素在于操作系统内TCP协议栈如何处理这个socket。
针对这个抓包分析,我们的结论就是:可能这里存在一种竞争条件(Race Condition)。如果操作系统关闭socket和收到对方发过来的FIN同时发生,那么决定这个socket进入TIME_WAIT还是CLOSED状态决定于 主动关闭请求(Tengine 程序针对 socket 调用 close 操作系统函数)和 被动关闭请求(操作系统内核线程收到 FIN 后调用的 tcp_v4_do_rcv 处理函数)哪个先发生 。
很多情况下,网络时延,CPU处理能力等各种环境因素不同,可能带来不同的结果。例如,而由于线下环境时延低,被动关闭可能最先发生;自从服务上云之后,Tengine跟后端Nginx的时延因为距离的原因被拉长了,因此Tengine主动关闭的情况更早进行,等等,导致了云上云下不一致的情况。
可是,如果目前的行为看起来都是符合协议标准的情况,那么如何正面解决这个问题就变得比较棘手了。我们无法通过降低Tengine所在的主机性能来延缓主动连接关闭请求,也无法降低因为物理距离而存在的时延消耗加快 FIN 请求的收取。这种情况下,我们会建议通过调整系统配置来缓解问题。
注:现在的Linux系统有很多方法都可以快速缓解该问题,例如,
a) 在timestamps启用的情况下,配置tw_reuse。
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
b) 配置 max_tw_buckets
net.ipv4.tcp_max_tw_buckets = 5000
缺点就是会往syslog里写: time wait bucket table overflow.
由于用户使用自建 Tengine ,且用户不愿意进行 TIME_WAIT 的强制清理,因此我们考虑通过Tengine的代码分析看看是否有机会在不改动 Tengine 源码的情况下,改变 Tengine 行为来避免socket被Tengine主动关闭。
Tengine version: Tengine/2.3.1
NGINX version: nginx/1.16.0
1、 Tengine code analysis
从之前的抓包,我们可以看出来多数的TIME_WAIT socket是为了后端健康检查而创建的,因此我们主要关注 Tengine的健康检查行为,以下是从ngx_http_upstream_check_module 的开源代码中摘抄出来的关于socket清理的函数。
从这段逻辑中,我们可以看到,如果满足以下任一条件时,Tengine会在收到数据包之后直接关闭连接。
- c->error != 0
- cf->need_keepalive = false
- c->requests > ucscf->check_keepalive_requ
这里,如果我们让以上的条件变成不满足,那么就有可能让Tengine所在的操作系统先处理被动关闭请求,进行socket清理,进入CLOSED状态,因为从HTTP1.0的协议上来说,NGINX服务器这一方一定会主动关闭连接。
2、解决方法
一般情况下,我们对于TIME_WAIT的连接无需太过关心,一般2MSL(默认60s) 之后,系统自动释放。如果需要减少,可以考虑长链接模式,或者调整参数。
该case中,客户对协议比较了解,但对于强制释放TIME_WAIT 仍有担心;同时由于后端存在1.8万台主机,长连接模式带来的开销更是无法承受。
因此,我们根据之前的代码分析,通过梳理代码里面的逻辑,推荐客户以下健康检查配置,
check interval=5000 rise=2 fall=2 timeout=3000 type=http default_down=false;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_keepalive_requests 2
check_http_expect_alive http_2xx http_3xx;
理由很简单,我们需要让之前提到的三个条件不满足。在代码中,我们不考虑 error 情况,而need_keepalive 在代码中默认 enable (如果不是,可以通过配置调整),因此需确保check_keepalive_requests大于1即可进入Tengine的KEEPALIVE逻辑,避免Tengine主动关闭连接。
因为使用HTTP1.0的HEAD方法,后端服务器收到后会主动关闭连接,因此Tengine创建的socket进入CLOSED状态,避免进入TIME_WAIT而占用动态端口资源。
作者:SRE团队技术小编-小凌
本文为阿里云原创内容,未经允许不得转载
解决Tengine健康检查引起的TIME_WAIT堆积问题的更多相关文章
- Tengine新增健康检查模块
总结 2.tengine的状态监控 Tengine的状态监控有两种 这里演示一个健康检查模块功能 配置一个status的location location /status { check_status ...
- Nginx负载均衡中后端节点服务器健康检查的操作梳理
正常情况下,nginx做反向代理,如果后端节点服务器宕掉的话,nginx默认是不能把这台realserver踢出upstream负载集群的,所以还会有请求转发到后端的这台realserver上面,这样 ...
- nginx之健康检查
正常情况下,nginx做反向代理,如果后端节点服务器宕掉的话,nginx默认是不能把这台realserver踢出upstream负载集群的,所以还会有请求转发到后端的这台realserver上面,这样 ...
- nginx.conf配置文件里的upstream加入健康检查
查看NGINX启用了那些模块: # ./nginx -V Tengine version: Tengine/ (nginx/) built by gcc (Red Hat -) (GCC) TLS S ...
- [转]Eureka自我保护机制、健康检查的作用、actuator模块监控
Eureka自我保护机制 接着以上篇文章建立的三个工程为基础(eureka-server,uerreg,myweb),默认Eureka是开启自我保护的.我们来做个测试,我们先启动三个工程,我们访问注册 ...
- Kubernetes应用健康检查
目录贴:Kubernetes学习系列 在实际生产环境中,想要使得开发的应用程序完全没有bug,在任何时候都运行正常,几乎 是不可能的任务.因此,我们需要一套管理系统,来对用户的应用程序执行周期性的健康 ...
- Nginx负载均衡中后端节点服务器健康检查的一种简单方式
摘自:https://cloud.tencent.com/developer/article/1027287 一.利用nginx自带模块ngx_http_proxy_module和ngx_http_u ...
- asp.net core 健康检查
asp.net core 健康检查 ASP.NET Core 2.2 开始,提供了健康检查中间件和库,用来报告应用基础结构组件的运行状况.官方文档在此 运行状况检查由应用程序作为 HTTP 终结点公开 ...
- nginx后端节点健康检查
一.nginx健康检查的三种方式 .ngx_http_proxy_module 模块和ngx_http_upstream_module模块(自带) 官网地址:http://nginx.org/en/d ...
随机推荐
- FreeSql.Repository (一)什么是仓储
欢迎来到<FreeSql.Repository 仓储模式>系列文档,完整文档请前往 wiki 中心:https://github.com/dotnetcore/FreeSql/wiki F ...
- word IF嵌套实现登记学生成绩(合格,良好,优秀)
word IF函数 IF语法 IF(判断条件,条件成立的动作,条件不成立的动作),以逗号(英文)分隔 例:=IF( 0 < 1 , "good" , "bad&qu ...
- mysql批量刷新用户密码
不知道用户密码,并且不改变用户密码的情况下,批量刷新MySQL数据库用户的密码 select concat('alter user \'',user,'\'@\'',host,'\' identifi ...
- python绘折线图
# -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as plt #X轴,Y轴数据 y = [0.3,0.4,2,5 ...
- Docker安装RabbitMQ与Kafka
RabbitMq安装(dokcer) 下载镜像 docker pull rabbitmq 创建并启动容器 docker run -d --name rabbitmq -p 5672:5672 -p 1 ...
- 牛客挑战赛46 B
题目链接: 最小的指数 乍一看还以为是Pollard_rho算法,其实大可不必. 发现\(1<= n <= 1e18\),我们可以将n分为两部分(分块思想降低时间复杂度). 剔除小于等于\ ...
- HDU4388-Stone Game II-Nim变形
http://acm.hdu.edu.cn/showproblem.php?pid=4388 Nim变形,对一个\(n\)个石子的堆,每次取\(k(0<k<n)\)个(注意不能全取光),同 ...
- Javascript 根据文件名判断是否未图片
var isImage = (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(filename)
- 论文阅读: A Review of Robot Learning for Manipulation: Challenges, Representations, and Algorithms
机器人学习操纵综述:挑战,表示形式和算法 1.介绍 因此,研究人员专注于机器人应如何学习操纵周围世界的问题. 这项研究的范围很广,从学习个人操作技巧到人类演示,再到学习适用于高级计划的操作任务的抽象描 ...
- Centos7安装packstack
Centos7安装packstack 步骤一 下载centos7.6 https://archive.kernel.org/centos-vault/7.6.1810/isos/x86_64/Cent ...