解决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 ...
随机推荐
- 微信开发中,不同手机系统遇到的bug(不定时更新)
Ios系统 1.body上绑定click事件失效. 解决:body标签下面,用个div,当做包裹所有内容的大容器.给这个div,绑定click事件. 2.不支持 YYYY-MM-DD 的时间格式. 用 ...
- asp.net-ajax使用-WebMethod使用
1.js $.ajax({ type: "POST", contentType: "application/json", url: "activity ...
- synchronized实现原理及ReentrantLock源码
synchronized synchronized的作用范围 public class SynchronizedTest { // 实例方法,方法访问标志ACC_SYNCHRONIZED,锁对象是对象 ...
- SQL注入-DNS注入(一)
这篇文章相对来说比较入门,参考的文章是:https://www.jianshu.com/p/c805209244c2 0x00前言 前段时间在做盲注 分别是基于时间和基于布尔型的 说真的 这两种盲注真 ...
- CTF练习 ①
最近学校要打比赛,,,把我这个混子也给算上了,,不得不赶紧学习学习. 今天学习的是SQL注入的一道题,参考的文章是 https://blog.csdn.net/qq_42939527/article ...
- angular8 大地老师学习笔记---第八课
/*ViewChild获取dom节点 1.模板中给dom起一个名字 <div #myBox> 我是一个dom节点 </div> 2.在业务逻辑里面引入ViewChild imp ...
- 从源码角度学习Java动态代理
前言 最近,看了一下关于RMI(Remote Method Invocation)相关的知识,遇到了一个动态代理的问题,然后就决定探究一下动态代理. 这里先科普一下RMI. RMI 像我们平时写的程序 ...
- Python 带你高效创作短视频,视频创作秀到飞起!!!
近两年,抖音.快手将短视频推到风口浪尖上,要生产出高质量的视频,离不开视频剪辑这一环节:在全民剪片浪潮中,大众使用最多的剪辑软件如:Pr.FCPX.剪印.Vue 等. 视频剪辑过程中,Python 一 ...
- 使用aspnet_compiler对web程序进行预编译
前言 本例使用的是asp.net中的webform项目,使用.net框架为.net3.5 操作步骤 正常的web项目发布步骤 发布方法:文件系统 目标位置:发布后的项目文件的路径,可自定义. 打开wi ...
- Fresco 二三事:图片处理之旋转、缩放、裁剪切割图片
关于Fresco加载图片的处理,例如旋转.裁剪切割图片,在官方文档也都有提到,只是感觉写的不太详细,正好最近项目里有类似需求,所以分享一些使用小tip,后面的朋友就不用再走弯路浪费时间了.(测试图片分 ...