[原创]结合案例深入解析orphan socket产生与消亡(一)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本文看点:结合服务器运行案例和TCP代码分析orphan socket产生与消亡以及对系统的影响。精彩的部分在(二)细节分析章节。 ##问题背景 tengine服务器发生过多次orphan socket数量很多的情况,例如有一次使用ss -s命令查看: ``` $ss -s T

原创文章,转载请注明:来自云栖社区

本文看点:结合服务器运行案例和TCP代码分析orphan socket产生与消亡以及对系统的影响。精彩的部分在(二)细节分析章节。

问题背景

tengine服务器发生过多次orphan socket数量很多的情况,例如有一次使用ss -s命令查看:

$ss -s
Total: 36569 (kernel 36823)
TCP:   142787 (estab 28586, closed 4842, orphaned 92575, synrecv 9198, timewait 4684/5073), ports 9868

总共142787条TCP连接,处于orphaned状态的连接达到92575。某天orphan socket数量监控占总量百分比曲线图如下:

2.png

10:30-10:49 约持续19分钟较高,然后恢复。

dmesg命令可以查看到有大量的提示:
[791.931967] Out of socket memory

orphan socket 做关键词google一把有很多,但没有查找到能详细说明来龙去脉的,

3.png

先来看一下在网上关于orphan sockets一段较好的介绍。

Do you have "too many" orphan sockets?
First of all: what's an orphan socket? It's simply a socket that isn't associated to a file descriptor. For instance, after you close() a socket, you no longer hold a file descriptor to reference it, but it still exists because the kernel has to keep it around for a bit more until TCP is done with it. Because orphan sockets aren't very useful to applications (since applications can't interact with them), the kernel is trying to limit the amount of memory consumed by orphans, and it does so by limiting the number of orphans that stick around. If you're running a frontend web server (or an HTTP load balancer), then you'll most likely have a sizeable number of orphans, and that's perfectly normal.

简要理解即orphan sockets是没有与任何文件描述符关联的socket,应用程序已经不能与此socket进行交互了,但是由于内核中TCP未完成,仍然占用TCP的内存暂时不能释放。读完这段还是丈二和尚摸不着头脑――摸不清底细。

对于维护线上稳定性需要搞清楚如下问题:

 什么原因或条件下会导致出现这么多orphan socket ?

 orphan socket的连接处于TCP状态的那一个阶段?

 orphan socket过多会给线上带来什么风险?

原因定位

以某台机器为例看一下TCP各状态的连接数量。

$ss -s
Total: 37964 (kernel 38570)
TCP:   146848 (estab 29556, closed 5114, orphaned 94904, synrecv 9402, timewait 4823/5226), ports 10946

orphaned数量达到 94904;

查看close调用后TCP连接各状态socket数量:(为什么直接查close调用后的状态请参细节分析部分,另外因以下命令执行时间上的偏差可能会对不上ss -s的数值)

$ss -nat | grep LAST-ACK | wc -l
67128

$ss -nat | grep FIN-WAIT-2 | wc -l
11712

$ss -nat | grep FIN-WAIT-1 | wc -l
12317

$ss -nat | grep CLOSE-WAIT | wc -l
25

$ss -nat | grep TIME-WAIT | wc -l
1410

top10输出分析ip:port的连接占用: 以下命令输出中第一列代表IP:PORT ,第二列代表TCP连接数量。

LAST_ACK状态的top10的IP与PORT输出

$ss -ant | grep LAST-ACK | awk "{++S[\$4]} END {for(a in S) print a, S[a]}" |sort -t " " -k 2 -n -r | head -n 10
10x.xxx.xxx.x38:51892 62190
10x.xxx.xxx.x38:51814 381
10x.xxx.xxx.x38:59206 97
10x.xxx.xxx.x38:56107 94
10x.xxx.xxx.x38:58271 92
10x.xxx.xxx.x38:59800 85
10x.xxx.xxx.x38:50851 55
10x.xxx.xxx.x38:52226 45
10x.xxx.xxx.x38:52199 27
10x.xxx.xxx.x38:57017 25

FIN-WAIT-1状态的top10的IP与PORT输出

$ss -ant | grep FIN-WAIT-1 | awk "{++S[\$4]} END {for(a in S) print a, S[a]}" |sort -t " " -k 2 -n -r | head -n 10
10x.xxx.xxx.x38:51892 5581
10x.xxx.xxx.x38:51814 1208
10x.xxx.xxx.x38:59206 782
10x.xxx.xxx.x38:58271 749
10x.xxx.xxx.x38:56107 724
10x.xxx.xxx.x38:59800 611
10x.xxx.xxx.x38:50851 285
10x.xxx.xxx.x38:52199 142
10x.xxx.xxx.x38:50127 97
10x.xxx.xxx.x38:52435 65

FIN-WAIT-2状态的top10的IP与PORT输出

$ss -ant | grep FIN-WAIT-2 | awk "{++S[\$4]} END {for(a in S) print a, S[a]}" |sort -t " " -k 2 -n -r | head -n 10
10.xxx.xxx.38:51814 1825
10.xxx.xxx.38:59800 1684
10.xxx.xxx.38:58271 1661
10.xxx.xxx.38:59206 1563
10.xxx.xxx.38:56107 1558
10.xxx.xxx.38:50851 329
10.xxx.xxx.38:52199 261
10.xxx.xxx.38:50127 203
10.xxx.xxx.38:52631 144
10.xxx.xxx.38:55488 114

可以明确看出这台主机上10x.xxx.xxx.x38:51892 占用了 62190 条socket处于LAST_ACK状态。
为什么这么多LAST_ACK状态的连接?看一下TCP状态迁移图,可知LAST_ACK状态代表被动关闭的一端在发送FIN报文后,最后等待另一端的ACK报文。

4.png

正常流程客户端TCP状态迁移:

CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
正常流程服务器TCP状态迁移:

CLOSED->LISTEN->SYN_RCVD->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

此机器是做为反向代理服务器符合上述正常流程服务器TCP状态迁移,第一个现象:通过监控查看10x.xxx.xxx.x38:51892对应的http流量,发现此用户每秒的新建TCP连接数量是1.5K~2K,且设置的流量带宽限制在20Mbps,并在报文出口方向出现大量的丢包;第二个现象:http访问日志中记录大量的http 499状态码的访问连接。

根据以上两个现象推断:10x.xxx.xxx.x38:51892收到client端的关闭FIN后会发送ACK,进入到CLOSE_WAIT 状态,应用程序中如果没有数据待发送会调用close()使TCP连接发送FIN,进入LAST_ACK状态,因为出口流量设置的限制大量的ACK和FIN报文被丢弃,导致出现上述62190 条socket处于LAST_ACK状态。

后来与用户沟通得知client端是移动端APP程序,其TCP连接读写超过(限流量没有收到数据等)会主动关闭连接,可以佐证日志中大量出现的499状态码。

到此原因分析完成,可以使用解决方案中调整服务器的内核参数解决,如果需要更详细了解orphaned socket可以详细阅读下一篇的细节分析。

解决方案

 如上细节分析中某些场景下会对当前sock做惩罚,将当前orphan 的数量 2x 甚至 4x与系统限制值做比较。这样内核日志会打印出Out of socket memory,但实际的数量并没有超过系统的限制值,属于误报的一种情形,可以根据具体的情况,将 tcp_max_orphans 内核参数值适当调大。

 系统中net.ipv4.tcp_orphan_retries 的默认值是 0,内核重置为8次, 需要将tcp_orphan_retries改为较小的数值,可大大降低orphans的数量,降低tcp内存的占用量。注意:设置较小的数值,可以有效降低orphans的数量(net.ipv4.tcp_orphan_retries = 0并不是想像中的不重试)

案例小结

内核日志dmesg命令看到 Out of socket memory,出现内存不足可能会有两种情况:

 有太多的 orphan sockets,通常对于一些负载较重的服务器经常会出现这种情况。

 分配给 TCP 的内存确实较少,从而导致内存不足。

第一种情况,可以用ss -s命令或cat /proc/net/sockstat查看是否孤儿套接字过多。

第二种情况,可以调整TCP的内存,因网上有大量的此类分析解决,这里不再详述。

最后来回答一下对于线上稳定性来说需要搞清楚的问题:

  1. orphan socket的连接处于TCP状态的那一个阶段?

TCP_FIN_WAIT1、TCP_LAST_ACK、TCP_CLOSING状态都可归类计数于orphan socket,但当通过TCP_LINGER2或sysctl_tcp_fin_timeout设置的超时时间大于60秒时的TCP_FIN_WAIT2的连接也归类计数于orphan socket;小于60秒的TCP_FIN_WAIT2状态的连接则归类计数于TIME_WAIT,从代码可以看出TCP_TIME_WAIT状态是不计入orphan socket;TCP_CLOSE_WAIT 状态的连接既不计入orphan socket 也不计入TIME_WAIT。

  1. 什么原因或条件下会导致出现这么多orphan socket ?
  2. orphan socket过多会给线上带来什么风险?

TCP_FIN_WAIT1 和 TCP_LAST_ACK 状态的连接都在等待对方回复ACK,例如client端可以对产生的连接故意发送FIN半关闭,而不回复最后的ACK使服务器端产生大量LAST_ACK,消耗TCP资源,这种情况下调整tcp_max_orphans参数和tcp_orphan_retries可以限制简单的DDOS攻击。

目录
相关文章
|
29天前
|
开发框架 .NET 中间件
.net8 使用 license 证书授权案例解析
本文介绍了如何使用 `.NET CLI` 创建并改造一个 `ASP.NET Core Web API` 项目,以实现基于许可证的授权机制。具体步骤包括创建项目、添加必要的 NuGet 包(如 `Standard.Licensing` 和 `Swashbuckle.AspNetCore`),以及修改 `Program.cs` 文件以集成自定义的许可证验证中间件。项目结构中新增了 `LicenseController` 接口用于处理授权相关操作,并通过测试流程验证了默认天气接口在未授权和授权状态下的响应情况。整个过程确保了应用程序能够在启动时正确验证许可证,保障系统的安全性与可控性。
67 8
.net8 使用 license 证书授权案例解析
|
15天前
|
机器学习/深度学习 人工智能 搜索推荐
技术革新下的培训新趋势:案例解析
从最初的“试试看”,到如今的“非做不可”,企业培训已经成为央国企和上市公司不可或缺的战略环节。无论是AI与大模型的赋能,DeepSeek,还是具身智能、智算技术和数据科学的实战应用,这些课程都在为企业打开新的可能性。
|
4月前
|
NoSQL Java Linux
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
323 76
|
4月前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
4月前
|
存储 监控 调度
云服务器成本优化深度解析与实战案例
本文深入探讨了云服务器成本优化的策略与实践,涵盖基本原则、具体策略及案例分析。基本原则包括以实际需求为导向、动态调整资源、成本控制为核心。具体策略涉及选择合适计费模式、优化资源配置、存储与网络配置、实施资源监控与审计、应用性能优化、利用优惠政策及考虑多云策略。文章还通过电商、制造企业和初创团队的实际案例,展示了云服务器成本优化的有效性,最后展望了未来的发展趋势,包括智能化优化、多云管理和绿色节能。
|
5月前
|
存储 人工智能 自然语言处理
高效档案管理案例介绍:文档内容批量结构化解决方案解析
档案文件内容丰富多样,传统人工管理耗时低效。思通数科AI平台通过自动布局分析、段落与标题检测、表格结构识别、嵌套内容还原及元数据生成等功能,实现档案的高精度分块处理和结构化存储,大幅提升管理和检索效率。某历史档案馆通过该平台完成了500万页档案的数字化,信息检索效率提升60%。
|
5月前
|
Prometheus 监控 Cloud Native
实战经验:成功的DevOps实施案例解析
实战经验:成功的DevOps实施案例解析
191 6
|
19天前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
83 29
|
15天前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
30 3
|
17天前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

热门文章

最新文章

推荐镜像

更多