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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 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攻击。

目录
相关文章
|
2月前
|
数据采集 人工智能 安全
数据治理的实践与挑战:大型案例解析
在当今数字化时代,数据已成为企业运营和决策的核心资源。然而,随着数据量的爆炸性增长和数据来源的多样化,数据治理成为了企业面临的重要挑战之一。本文将通过几个大型案例,探讨数据治理的实践、成效以及面临的挑战。
数据治理的实践与挑战:大型案例解析
|
16天前
|
存储 监控 调度
云服务器成本优化深度解析与实战案例
本文深入探讨了云服务器成本优化的策略与实践,涵盖基本原则、具体策略及案例分析。基本原则包括以实际需求为导向、动态调整资源、成本控制为核心。具体策略涉及选择合适计费模式、优化资源配置、存储与网络配置、实施资源监控与审计、应用性能优化、利用优惠政策及考虑多云策略。文章还通过电商、制造企业和初创团队的实际案例,展示了云服务器成本优化的有效性,最后展望了未来的发展趋势,包括智能化优化、多云管理和绿色节能。
|
1月前
|
存储 人工智能 自然语言处理
高效档案管理案例介绍:文档内容批量结构化解决方案解析
档案文件内容丰富多样,传统人工管理耗时低效。思通数科AI平台通过自动布局分析、段落与标题检测、表格结构识别、嵌套内容还原及元数据生成等功能,实现档案的高精度分块处理和结构化存储,大幅提升管理和检索效率。某历史档案馆通过该平台完成了500万页档案的数字化,信息检索效率提升60%。
|
1月前
|
Prometheus 监控 Cloud Native
实战经验:成功的DevOps实施案例解析
实战经验:成功的DevOps实施案例解析
55 6
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
24 1
|
2月前
|
数据格式
常用的Lambda表达式案例解析,工作中都会用到!
常用的Lambda表达式案例解析,工作中都会用到!
|
2月前
|
存储 数据采集 监控
CDGA\如何建立实现数据治理的效率价值框架:实践案例解析
数据治理是一个持续优化的过程。组织应建立健全的监督与评估机制,定期对数据治理工作进行评估,发现问题及时整改。广东药科大学通过数据全景图和数据监控大屏,实现了对数据治理成果的动态、多维度呈现与监控,为科学管理决策提供了有力支撑。
|
2月前
|
网络协议 测试技术 网络安全
Python编程-Socket网络编程
Python编程-Socket网络编程
30 0
|
5月前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
61 4
|
5月前
|
开发者 Python
Python Socket编程:不只是基础,更有进阶秘籍,让你的网络应用飞起来!
【7月更文挑战第25天】在网络应用蓬勃发展的数字时代,Python凭借其简洁的语法和强大的库支持成为开发高效应用的首选。本文通过实时聊天室案例,介绍了Python Socket编程的基础与进阶技巧,包括服务器与客户端的建立、数据交换等基础篇内容,以及使用多线程和异步IO提升性能的进阶篇。基础示例展示了服务器端监听连接请求、接收转发消息,客户端连接服务器并收发消息的过程。进阶部分讨论了如何利用Python的`threading`模块和`asyncio`库来处理多客户端连接,提高应用的并发处理能力和响应速度。掌握这些技能,能使开发者在网络编程领域更加游刃有余,构建出高性能的应用程序。
37 3

推荐镜像

更多
下一篇
DataWorks