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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
云解析 DNS,旗舰版 1个月
公共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攻击。

目录
相关文章
|
7天前
|
NoSQL Java Linux
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
142 75
|
6天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
24天前
|
存储 监控 调度
云服务器成本优化深度解析与实战案例
本文深入探讨了云服务器成本优化的策略与实践,涵盖基本原则、具体策略及案例分析。基本原则包括以实际需求为导向、动态调整资源、成本控制为核心。具体策略涉及选择合适计费模式、优化资源配置、存储与网络配置、实施资源监控与审计、应用性能优化、利用优惠政策及考虑多云策略。文章还通过电商、制造企业和初创团队的实际案例,展示了云服务器成本优化的有效性,最后展望了未来的发展趋势,包括智能化优化、多云管理和绿色节能。
|
1月前
|
存储 人工智能 自然语言处理
高效档案管理案例介绍:文档内容批量结构化解决方案解析
档案文件内容丰富多样,传统人工管理耗时低效。思通数科AI平台通过自动布局分析、段落与标题检测、表格结构识别、嵌套内容还原及元数据生成等功能,实现档案的高精度分块处理和结构化存储,大幅提升管理和检索效率。某历史档案馆通过该平台完成了500万页档案的数字化,信息检索效率提升60%。
|
1月前
|
Prometheus 监控 Cloud Native
实战经验:成功的DevOps实施案例解析
实战经验:成功的DevOps实施案例解析
71 6
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
25 1
|
2月前
|
数据格式
常用的Lambda表达式案例解析,工作中都会用到!
常用的Lambda表达式案例解析,工作中都会用到!
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
83 2
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
84 0
|
6天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析

热门文章

最新文章

推荐镜像

更多