简介: 解决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端口号,进行了过滤。

图1:一次HTTP请求交互过程

虽然上面的抓包结果显示当前 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清理的函数。

图2:Tengine 健康检查完成后清理socket过程

从这段逻辑中,我们可以看到,如果满足以下任一条件时,Tengine会在收到数据包之后直接关闭连接。

  • c->error != 0
  • cf->need_keepalive = false
  • c->requests > ucscf->check_keepalive_requ
图3: Tengine 中真正完成socket关闭的函数

这里,如果我们让以上的条件变成不满足,那么就有可能让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主动关闭连接。

图4:Tengine健康检查参考配置

因为使用HTTP1.0的HEAD方法,后端服务器收到后会主动关闭连接,因此Tengine创建的socket进入CLOSED状态,避免进入TIME_WAIT而占用动态端口资源。

作者:SRE团队技术小编-小凌

原文链接

本文为阿里云原创内容,未经允许不得转载

解决Tengine健康检查引起的TIME_WAIT堆积问题的更多相关文章

  1. Tengine新增健康检查模块

    总结 2.tengine的状态监控 Tengine的状态监控有两种 这里演示一个健康检查模块功能 配置一个status的location location /status { check_status ...

  2. Nginx负载均衡中后端节点服务器健康检查的操作梳理

    正常情况下,nginx做反向代理,如果后端节点服务器宕掉的话,nginx默认是不能把这台realserver踢出upstream负载集群的,所以还会有请求转发到后端的这台realserver上面,这样 ...

  3. nginx之健康检查

    正常情况下,nginx做反向代理,如果后端节点服务器宕掉的话,nginx默认是不能把这台realserver踢出upstream负载集群的,所以还会有请求转发到后端的这台realserver上面,这样 ...

  4. nginx.conf配置文件里的upstream加入健康检查

    查看NGINX启用了那些模块: # ./nginx -V Tengine version: Tengine/ (nginx/) built by gcc (Red Hat -) (GCC) TLS S ...

  5. [转]Eureka自我保护机制、健康检查的作用、actuator模块监控

    Eureka自我保护机制 接着以上篇文章建立的三个工程为基础(eureka-server,uerreg,myweb),默认Eureka是开启自我保护的.我们来做个测试,我们先启动三个工程,我们访问注册 ...

  6. Kubernetes应用健康检查

    目录贴:Kubernetes学习系列 在实际生产环境中,想要使得开发的应用程序完全没有bug,在任何时候都运行正常,几乎 是不可能的任务.因此,我们需要一套管理系统,来对用户的应用程序执行周期性的健康 ...

  7. Nginx负载均衡中后端节点服务器健康检查的一种简单方式

    摘自:https://cloud.tencent.com/developer/article/1027287 一.利用nginx自带模块ngx_http_proxy_module和ngx_http_u ...

  8. asp.net core 健康检查

    asp.net core 健康检查 ASP.NET Core 2.2 开始,提供了健康检查中间件和库,用来报告应用基础结构组件的运行状况.官方文档在此 运行状况检查由应用程序作为 HTTP 终结点公开 ...

  9. nginx后端节点健康检查

    一.nginx健康检查的三种方式 .ngx_http_proxy_module 模块和ngx_http_upstream_module模块(自带) 官网地址:http://nginx.org/en/d ...

随机推荐

  1. 彻底理解Hive中的锁

    前面遇到过一次因为Hive中表被锁住了,导致定时任务一直失败.这两天又出现了表被锁,原因是连接hiveserver2过于频繁,mysql连接被打满,引发的连锁反应,导致我们的小时任务一直失败,下午重点 ...

  2. 如何优雅地使用云原生 Prometheus 监控集群

    作者陈凯烨,腾讯云前端开发工程师.负责 TKE 集群,弹性集群和云原生监控等模块控制台开发. 概述 Prometheus 是一套开源的系统监控报警框架.2016 年,Prometheus 正式加入 C ...

  3. 海选与包装,Python中常用的两个高阶函数(讲义)

    一.filter(function, iterable) - 过滤("海选") # 判断落在第一象限的点[(x1, y1), (x2, y2)...] points = [(-1, ...

  4. linux不同环境变量文件的比较,如/etc/profile和/etc/environment

    /etc/profile 为系统的每个用户设置环境信息和启动程序,当用户第一次登录时,该文件被执行,其配置对所有登录的用户都有效. 当被修改时,必须重启才会生效.英文描述:"System w ...

  5. Linux嵌入式学习-mount命令+nfs挂载失败原因【转】

    NFS 挂载失败原因[待搜集] 1.挂载时若出现mount.nfs: Input/output error 解决:在客户端也需启动portmap服务 service portmap status[查看 ...

  6. win10 设置文件夹别名、修改文件夹图标、修改文件夹别名、英文目录和中文目录、设置文件夹中文名称、快捷访问显示设置中文

    最近在设置文件夹的时候发现个有趣的事情: 系统路径 C:\Users\Administrator  内的文件夹不仅有图标还显示中文名称,但是打开路径的时候显示的却是英文,这就激发了我的探索欲,究竟是为 ...

  7. 使用Arduino点亮ESP-01S,ESP8266-01S上的板载LED

    因为在开发ESP-01s远程控制中觉得接线麻烦,又因为ESP-01s板子上带有LED灯,那就先点亮板载LED,  如图所示: 打开Arduino 把代码copy进去,再编译烧录,就可以看见LED灯每隔 ...

  8. sql语句查询,limit与order by 同时出现,应该order by在前面

    eg:select orderid,status,createtime from orders where appid = :appId and userid = :userId order by c ...

  9. 论文翻译:2018_Deep Learning for Acoustic Echo Cancellation in Noisy and Double-Talk Scenarios

    论文地址:深度学习用于噪音和双语场景下的回声消除 博客地址:https://www.cnblogs.com/LXP-Never/p/14210359.html 摘要 传统的声学回声消除(AEC)通过使 ...

  10. spring: 我是如何解决循环依赖的?

    1.由同事抛的一个问题开始 最近项目组的一个同事遇到了一个问题,问我的意见,一下子引起的我的兴趣,因为这个问题我也是第一次遇到.平时自认为对spring循环依赖问题还是比较了解的,直到遇到这个和后面的 ...