简介: 解决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. 前端进阶之认识与手写compose方法

    目录 前言:为什么要学习这个方法 compose简介 compose的实现 最容易理解的实现方式 手写javascript中reduce方法 redux中compose的实现 参考文章 最后 前言:为 ...

  2. sqli-labs less-24(二次注入)

    less-24 原理: 在网站处理用户提交的数据的时候,只是将某些敏感字符进行了转义.因而使得用户第一次提交的时候不会被当做代码执行.但是这些数据存入数据库的时候却没有转义,而网站程序默认数据库中的数 ...

  3. JavaSE03-运算符&分支语句

    1.运算符 1.1 算术运算符 1.1.1 运算符和表达式 运算符:对常量或者变量进行操作的符号 表达式:用运算符把常量或者变量连接起来符合java语法的式子就可以称为表达式. 不同运算符连接的表达式 ...

  4. DHCP spooping非法获取地址设置原理

    一.DHCP概述 DHCP(动态主机配置协议)是一个局域网的网络协议.指的是由服务器控制一段IP地址范围,客户机登录服务器时就 可以自动获得服务器分配的IP地址和子网掩码.默认情况下,DHCP作为Wi ...

  5. css 09-CSS案例讲解:博雅互动

    09-CSS案例讲解:博雅互动 #前言 CSS已经学了一些基础内容了,我们来讲解一个小案例吧.以博雅互动的官网首页举例. #版心 首页的版心如下: 这里我们要普及一个概念,叫"版心" ...

  6. Flink批处理读写Hive

    import org.apache.flink.table.api.*; import org.apache.flink.table.catalog.hive.HiveCatalog; /** * @ ...

  7. Logistic 回归-原理及应用

    公号:码农充电站pro 主页:https://codeshellme.github.io 上一篇文章介绍了线性回归模型,它用于处理回归问题. 这次来介绍一下 Logistic 回归,中文音译为逻辑回归 ...

  8. Selenium Web元素定位方法

    Selenium是用于Web应用测试的自动化测试框架,可以实现跨浏览器和跨平台的Web自动化测试.Selenium通过使用WebDriver API来控制web浏览器,每个浏览器都都有一个特定的Web ...

  9. Autofac的基本使用---3、泛型类型

    Autofac的基本使用---目录 准备 使用的表是Teacher,创建相关的IDAL.DAL.IBLL.BLL层. 使用EF,创建一个Model层,存放edmx文件. 创建一个Infrastruct ...

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

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