从问题看本质 研究TCP close wait的内幕

2024-05-01 15:58

本文主要是介绍从问题看本质 研究TCP close wait的内幕,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

最近遇到的一个关于socket.close的问题,在某个应用服务器出现的状况(执行netstat -np | grep tcp): 

tcp        0      0 10.224.122.16:50158         10.224.112.58:8788          CLOSE_WAIT

tcp        0      0 10.224.122.16:37655         10.224.112.58:8788          CLOSE_WAIT

tcp        1      0 127.0.0.1:32713             127.0.0.1:8080              CLOSE_WAIT

tcp       38      0 10.224.122.16:34538         10.224.125.42:443           CLOSE_WAIT

tcp       38      0 10.224.122.16:33394         10.224.125.42:443           CLOSE_WAIT

tcp        1      0 10.224.122.16:18882         10.224.125.10:80            CLOSE_WAIT

tcp        1      0 10.224.122.16:18637         10.224.125.10:80            CLOSE_WAIT

tcp        1      0 10.224.122.16:19655         10.224.125.12:80            CLOSE_WAIT

........................................

 

总共出现了200个CLOSE_WAIT的socket.而且这些socket长时间得不到释放.下面我们来看看为什么会出现这种大量socket的CLOSE_WAIT情况

 

首先我们要搞清楚的是,这个socket是谁发起的,我们可以看到122.16这台机器开了很多端口,而且端口号都很大,125.12 或者125.10上的端口都是很常见服务器端口,所以122.16上这么多CLOSE_WAIT

的socket是由122.16开启的,换句话说这台机器是传统的客户端,它会主动的请求其他机器的服务端口.

 

要搞清楚为什么会出现CLOSE_WAIT,那么首先我们必须要清楚CLOSE_WAIT的机制和原理.

 

假设我们有一个client, 一个server.

 

当client主动发起一个socket.close()这个时候对应TCP来说,会发生什么事情呢?如下图所示.

 

 

 

client首先发送一个FIN信号给server, 这个时候client变成了FIN_WAIT_1的状态, server端收到FIN之后,返回ACK,然后server端的状态变成了CLOSE_WAIT.

接着server端需要发送一个FIN给client,然后server端的状态变成了LAST_ACK,接着client返回一个ACK,然后server端的socket就被成功的关闭了.

 

从这里可以看到,如果由客户端主动关闭一链接,那么客户端是不会出现CLOSE_WAIT状态的.客户端主动关闭链接,那么Server端将会出现CLOSE_WAIT的状态.

而我们的服务器上,是客户端socket出现了CLOSE_WAIT,由此可见这个是由于server主动关闭了server上的socket.

 

那么当server主动发起一个socket.close(),这个时候又发生了一些什么事情呢.

 

从图中我们可以看到,如果是server主动关闭链接,那么Client则有可能进入CLOSE_WAIT,如果Client不发送FIN包,那么client就一直会处在CLOSE_WAIT状态(后面我们可以看到有参数可以调整这个时间).

 

那么现在我们要搞清楚的是,在第二中场景中,为什么Client不发送FIN包给server.要搞清楚这个问题,我们首先要搞清楚server是怎么发FIN包给client的,其实server就是调用了

socket.close方法而已,也就是说如果要client发送FIN包,那么client就必须调用socket.close,否则就client就一直会处在CLOSE_WAIT(但事实上不同操作系统这点的实现还不一样,

在ahuaxuan(ahuaxuan.iteye.com)的例子中也出现了这样的case).

 

下面我们来做几个实验

实验一:

环境:

服务器端:win7+tomcat,tomcat的keep-alive的时间为默认的15s.

客户端:mac os

实验步骤:服务器启动后,客户端向服务器发送一个get请求,然后客户端阻塞,等待服务器端的socket超时.通过netstat -np tcp可以看到的情况是发送get请求时,服务器和客户端链接是ESTABLISHED, 15s之后,客户端变成了CLOSE_WAIT,而服务器端变成了FIN_WAIT_2.这一点也在我们的预料之中,而这个时候由于客户端线程阻塞,客户端socket空置在那里,不做任何操作,2分钟过后,这个链接不管是在win7上,还是在mac os都看不到了.可见,FIN_WAIT_2或者CLOSE_WAIT有一个timeout.在后面的实验,可以证明,在这个例子中,其实是FIN_WAIT_2有一个超时,一旦过了2分钟,那么win7会发一个RST给mac os要求关闭双方的socket.


 

总结, 当服务器的内核不一样上FIN_WAIT_2的超时时间和操作是不一样的.

经查:控制FIN_WAIT_2的参数为:

/proc/sys/net/ipv4/tcp_fin_timeout

如 果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。参见tcp_max_orphans。

 


 

但是我们的问题是客户端有很多CLOSE_WAIT,而且我们的服务器不是windows,而是linux,所以CLOSE_WAIT有没有超时时间呢,肯定有,而且默认情况下这个超时时间应该是比较大的.否则不会一下子看到两百个CLOSE_WAIT的状态.

 

客户端解决方案:

 

1.由于socket.close()会导致FIN信号,而client的socket CLOSE_WAIT就是因为该socket该关的时候,我们没有关,所以我们需要一个线程池来检查空闲连接中哪些进入了超时状态(idleTIME),但进入超时

的socket未必是CLOSE_WAIT的状态的.不过如果我们把空闲超时的socket关闭,那么CLOSE_WAIT的状态就会消失.(问题:像HttpClient这样的工具包中,如果要检查链接池,那么则需要锁定整个池,而这个时候,用户请求获取connection的操作只能等待,在高并发的时候会造成程序响应速度下降,具体参考IdleConnectionTimeoutThread.java(HttpClient3.1))

 

2.经查,其实有参数可以调整CLOSE_WAIT的持续时间,如果我们改变这个时间,那么可以让CLOSE_WAIT只保持很短的时间(当然这个参数不只作用在CLOSE_WAIT上,缩短这个时间可能会带来其他的影响).在客户端机器上修改如下:

sysctl -w net.ipv4.tcp_keepalive_time=60(缺省是2小时,现在改成了60秒)

sysctl -w net.ipv4.tcp_keepalive_probes=2

sysctl -w net.ipv4.tcp_keepalive_intvl=2

我们将CLOSE_WAIT的检查时间设置为30s,这样一个CLOSE_WAIT只会存在30S.

 

3. 当然,最重要的是我们要检查客户端链接的空闲时间,空闲时间可以由客户端自行定义,比如idleTimeout,也可由服务器来决定,服务器只需要每次在response.header中加入一个头信息,比如说名字叫做timeout头,当然一般情况下我们会用keep-alive这个头字段, 如果服务器设置了该字段,那么客户端拿到这个属性之后,就知道自己的connection最大的空闲时间,这样不会由于服务器关闭socket,而导致客户端socket一直close_wait在那里.

 

服务器端解决方案

 

4.前面讲到客户端出现CLOSE_WAIT是由于服务器端Socket的读超时,也是TOMCAT中的keep-alive参数.那么如果我们把这个超时时间设置的长点,会有什么影响?

如果我们的tomcat既服务于浏览器,又服务于其他的APP,而且我们把connection的keep-alive时间设置为10分钟,那么带来的后果是浏览器打开一个页面,然后这个页面一直不关闭,那么服务器上的socket也不能关闭,它所占用的FD也不能服务于其他请求.如果并发一高,很快服务器的资源将会被耗尽.新的请求再也进不来. 那么如果把keep-alive的时间设置的短一点呢,比如15s? 那么其他的APP来访问这个服务器的时候,一旦这个socket, 15s之内没有新的请求,那么客户端APP的socket将出现大量的CLOSE_WAIT状态.

所以如果出现这种情况,建议将你的server分开部署,服务于browser的部署到单独的JVM实例上,保持keep-alive为15s,而服务于架构中其他应用的功能部署到另外的JVM实例中,并且将keep-alive的时间设置的更

长,比如说1个小时.这样客户端APP建立的connection,如果在一个小时之内都没有重用这条connection,那么客户端的socket才会进入CLOSE_WAIT的状态.针对不同的应用场景来设置不同的keep-alive时间,可以帮助我们提高程序的性能.

 

5.如果我们的应用既服务于浏览器,又服务于其他的APP,那么我们还有一个终极解决方案.

那就是配置多个connector, 如下:

<!-- for browser -->

 <Connector port="8080" protocol="HTTP/1.1" 

               connectionTimeout="20000" 

               redirectPort="8443" />

 

<!-- for other APP -->

<Connector port="8081" protocol="HTTP/1.1" 

               connectionTimeout="20000" 

               redirectPort="8443" keepAliveTimeout="330000" />

 

访问的时候,浏览器使用8080端口,其他的APP使用8081端口.这样可以保证浏览器请求的socket在15s之内如果没有再次使用,那么tomcat会主动关闭该socket,而其他APP请求的socket在330s之内没有使用,才关闭该socket,这样做可以大大减少其他APP上出现CLOSE_WAIT的几率.

 

你一定会问,如果我不设置keepAliveTimeout又怎么样呢,反正客户端有idleTimeout,客户端的close_wait不会持续太长时间,请注意看上图中标红的地方,一个是close_wait,还有一个是time_wait状态,也就是说谁主动发起请求,那么它将会最终进入time_wait状态,据说windows上这个time_wait将持续4分钟,我在linux上的测试表明,linux上它大概是60s左右,也就是说高并发下,也就是服务器也需要过60s左右才能真正的释放这个FD.所以我们如果提供http服务给其他APP,那么我们最好让客户端优先关闭socket,也就是将客户端的idleTimeout设置的比server的keepalivetimeout小一点.这样保证time_wait出现在客户端. 而不是资源较为紧张的服务器端.


 

 

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
这里写图片描述

这篇关于从问题看本质 研究TCP close wait的内幕的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/952079

相关文章

MybatisGenerator文件生成不出对应文件的问题

《MybatisGenerator文件生成不出对应文件的问题》本文介绍了使用MybatisGenerator生成文件时遇到的问题及解决方法,主要步骤包括检查目标表是否存在、是否能连接到数据库、配置生成... 目录MyBATisGenerator 文件生成不出对应文件先在项目结构里引入“targetProje

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

SpringBoot项目删除Bean或者不加载Bean的问题解决

《SpringBoot项目删除Bean或者不加载Bean的问题解决》文章介绍了在SpringBoot项目中如何使用@ComponentScan注解和自定义过滤器实现不加载某些Bean的方法,本文通过实... 使用@ComponentScan注解中的@ComponentScan.Filter标记不加载。@C