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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 本文看点:结合服务器运行案例和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攻击。

目录
相关文章
|
4天前
|
缓存 网络协议 Linux
DNS解析工具使用案例
关于如何在Windows和Linux操作系统下使用DNS解析工具的案例,包括查看和清空DNS缓存、使用whois查询工具以及安装和使用dig工具进行DNS记录查询。
9 2
DNS解析工具使用案例
|
22天前
|
人工智能 PyTorch 算法框架/工具
Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程
【8月更文挑战第6天】Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程
Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程
|
13天前
|
JavaScript 前端开发 UED
Javaweb之javascript的小案例的详细解析
通过上述步骤,我们得到了一个动态更新的实时时钟,这个简单的JavaScript案例展示了定时器的使用方法,并讲解了如何处理日期和时间。这个案例说明了JavaScript在网页中添加动态内容与交互的能力。对于涉足JavaWeb开发的学习者来说,理解和运用这些基础知识非常重要。
31 11
|
20天前
|
网络协议 Java
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
这篇文章全面讲解了基于Socket的TCP网络编程,包括Socket基本概念、TCP编程步骤、客户端和服务端的通信过程,并通过具体代码示例展示了客户端与服务端之间的数据通信。同时,还提供了多个案例分析,如客户端发送信息给服务端、客户端发送文件给服务端以及服务端保存文件并返回确认信息给客户端的场景。
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
|
7天前
|
开发者
告别繁琐代码,JSF标签库带你走进高效开发的新时代!
【8月更文挑战第31天】JSF(JavaServer Faces)标准标签库为页面开发提供了大量组件标签,如`<h:inputText>`、`<h:dataTable>`等,简化代码、提升效率并确保稳定性。本文通过示例展示如何使用这些标签实现常见功能,如创建登录表单和展示数据列表,帮助开发者更高效地进行Web应用开发。
15 0
|
7天前
|
UED 存储 自然语言处理
【语言无界·体验无疆】解锁Vaadin应用全球化秘籍:从代码到文化,让你的应用畅游世界每一个角落!
【8月更文挑战第31天】《国际化与本地化实战:构建多语言支持的Vaadin应用》详细介绍了如何使用Vaadin框架实现应用的国际化和本地化,提升用户体验和市场竞争力。文章涵盖资源文件的创建与管理、消息绑定与动态加载、日期和数字格式化及文化敏感性处理等方面,通过具体示例代码和最佳实践,帮助开发者构建适应不同语言和地区设置的Vaadin应用。通过这些步骤,您的应用将更加灵活,满足全球用户需求。
19 0
|
7天前
|
前端开发 API 开发者
【React状态管理新思路】Context API入门:从零开始摆脱props钻孔的优雅之道,全面解析与实战案例分享!
【8月更文挑战第31天】React 的 Context API 有效解决了多级组件间状态传递的 "props 钻孔" 问题,使代码更简洁、易维护。本文通过电子商务网站登录状态管理案例,详细介绍了 Context API 的使用方法,包括创建、提供及消费 Context,以及处理多个 Context 的场景,适合各水平开发者学习与应用,提高开发效率和代码质量。
12 0
|
2月前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
27 4
|
2月前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
【7月更文挑战第26天】在网络的数字宇宙中,Python Socket编程是开启网络世界大门的钥匙。本指南将引领你从基础到实战,成为网络世界的建筑师。
49 2
|
2月前
|
网络协议 程序员 视频直播

热门文章

最新文章

推荐镜像

更多
下一篇
DDNS