socket-详细分析No buffer space available

关键词:socket,tcp三次握手,tcp四次握手,2MSL最大报文生存时间,LVS,负载均衡前端

 

新年上班第一天,忽然遇到一个socket链接No buffer space available的问题,致使接口大面积调用(webservice,httpclient)失败的问题,重启服务器后又恢复了正常java

问题详情

具体异常栈信息以下:web

Caused by: java.net.SocketException: No buffer space available (maximum connections reached?): connect

at org.apache.axis.AxisFault.makeFault(AxisFault.java:101)

at org.apache.axis.transport.http.HTTPSender.invoke(HTTPSender.java:154)

at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)

at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)

at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)

at org.apache.axis.client.AxisClient.invoke(AxisClient.java:165)

at org.apache.axis.client.Call.invokeEngine(Call.java:2784)

at org.apache.axis.client.Call.invoke(Call.java:2767)

at org.apache.axis.client.Call.invoke(Call.java:2443)

at org.apache.axis.client.Call.invoke(Call.java:2366)

at org.apache.axis.client.Call.invoke(Call.java:1812)

 

Caused by: java.net.SocketException: No buffer space available (maximum connections reached?): connect

at java.net.PlainSocketImpl.socketConnect(Native Method)

at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)

at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)

at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)

at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)

at java.net.Socket.connect(Socket.java:519)

at sun.reflect.GeneratedMethodAccessor24.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:597)

at org.apache.axis.components.net.DefaultSocketFactory.create(DefaultSocketFactory.java:153)

at org.apache.axis.components.net.DefaultSocketFactory.create(DefaultSocketFactory.java:120)

at org.apache.axis.transport.http.HTTPSender.getSocket(HTTPSender.java:191)

at org.apache.axis.transport.http.HTTPSender.writeToSocket(HTTPSender.java:404)

at org.apache.axis.transport.http.HTTPSender.invoke(HTTPSender.java:138)

查阅了网上的资料,基本能够把问题锁定在:系统并发过大,链接数过多,部分socket链接没法释放关闭,而持续请求又致使没法释放的socket链接不断积压,最终致使No buffer space available算法

最快解决办法

最快的解决办法:重启服务器,注意,重启tomcat不起做用。下面将分析最终的解决办法apache

问题分析

虽然重启服务器能最快的将socket链接释放,可是问题很容易复现,很明显这不是问题的根本解决方式。还有几个问题须要进行进一步分析:windows

 

l 打开cmd输入netstat -an,发现存在大量处于TIME_WAIT状态的TCP链接,也就是以前提到的未释放的socket链接,而且server端口在不断变化,这又是什么现象呢?以下如图后端

 

l 系统是否有自动关闭链接的措施,是代码问题仍是性能问题?tomcat

 

下面咱们来分析解决这几个问题。安全

 

TIME_WAIT状态的由来

 

咱们知道,TCP关闭链接须要通过四次握手,为何是四次握手,而不是像创建链接那样三次握手,看看下面三次握手和四次握手的流程图。服务器

 

              三次握手创建链接示意图

 

              四次握手关闭链接示意图

 

从上面的三次握手创建链接示意图中能够知道,只要client端和server端都接收到了对方发送的ACK应答以后,双方就能够创建链接,以后就能够进行数据交互了,这个过程须要三步。

 

而四次握手关闭链接示意图中,TCP协议中,关闭TCP链接的是Server端(固然,关闭均可以由任意一方发起),当Server端发起关闭链接请求时,向Client端发送一个FIN报文,Client收到FIN报文时,极可能还有数据须要发送,因此并不会当即关闭SOCKET,因此先回复一个ACK报文,告诉Server端,“你发的FIN报文我收到了”。当Client端的全部报文都发送完毕以后,Client端向Server端发送一个FIN报文,此时Client端进入关闭状态,不在发送数据。

 

Server端收到FIN报文后,就知道能够关闭链接了,可是网络是不可靠的,Client端并不知道Server端要关闭,因此Server端发送ACK后进入TIME_WAIT状态,若是Client端没有收到ACKServer能够从新发送。Client端收到ACK后,就知道能够断开链接了。Server端等待了2MSL(Max Segment Lifetime最大报文生存时间)后依然没有收到回复,则证实Client端已正常断开,此时,Server端也能够断开链接了。2MSL的TIME_WAIT等待时间就是由此而来。

 

咱们知道了TIME_WAIT的由来,TIME_WAIT 状态最大保持时间是2 * MSL在1-4分钟之间,因此当系统并发过大,Client-Server链接数过多,Server端会在1-4分钟以内积累大量处于TIME_WAIT状态的没法释放的socket链接致使服务器效率急剧降低,甚至耗完服务器的全部资源,最终致使No buffer space available (maximum connections reached?): connect

问题的发生。

 

端口变化由来

 

对于大型的应用,访问量较高,一台Server每每不能知足服务需求,这时就须要多台Server共同对外提供服务。如何充分、最大的利用多台Server的资源处理请求,这时就须要请求调度,将请求合理均匀的分配到各台Server

 

LVS (Linux Virtual Server)集群(Cluster)技术就是实现这一需求的方式之一。采用IP负载均衡技术和基于内容请求分发技术。调度器具备很好的吞吐率,将请求均衡地转移到不一样的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。

LVS集群采用三层结构,其主要组成部分为:

负载均衡调度器load balancer),它是整个集群对外面的前端机,负责将客户的请求发送到一组服务器上执行,而客户认为服务是来自一个IP地址(咱们可称之为虚拟IP地址)上的。

服务器池server pool),是一组真正执行客户请求的服务器,执行的服务有WEBMAILFTPDNS等。

共享存储shared storage),它为服务器池提供一个共享的存储区,这样很容易使得服务器池拥有相同的内容,提供相同的服务。

其结构以下图所示:

 

              LVS结构示意图

 

LVS结构示意图中能够看出,Load Balancer到后端ServerIP的数据包的 源IP地址都是同样Load BalancerIP地址和Server IP地址属于同一网段),而客户端认为服务是来自一个IP地址(实际上就是Load BalancerIP),频繁的TCP链接创建和关闭,使得Load Balancer到后端ServerTCP链接会受到限制,致使在server上留下不少处于TIME_WAIT状态的链接,并且这些状态对应的远程IP地址都是Load Balancer的。Load Balancer的端口最多也就60000多个(2^16=65536,1~1023是保留端口,还有一些其余端口缺省也不会用),每一个Load Balancer上的端口一旦进入 ServerTIME_WAIT黑名单,就有240秒不能再用来创建和Server的链接,这样Load Balancer和Server的链接就颇有限。因此咱们看到了使用netstat -an命令查看网络链接情况时同一个 remote IP会有不少端口。

最终解决办法

从上面的分析来看,致使出现No buffer space available这一问题的缘由是多方面的,缘由以及解决办法以下:

 

从代码层面上看,webservicehttpclient调用未进行链接释放,致使资源没法回收

 

解决办法是在axis2的客户端代码中进行链接关闭,以下:

stub._getServiceClient().cleanupTransport();
   stub._getServiceClient().cleanup();
    stub.cleanup();
    stub = null;

及时的关闭和clean能有效的避免内存溢出的问题,及时回收资源。

或者httpClient中,最终要在finally调用response.close()或者httpPost.releaseConnection() 进行链接释放。

 

从系统层面上看,系统socket链接数设置不合理,socket链接数太小,易达到上限;其次是2MSL设置过长,容易积压TIME_WAIT状态的TCP链接

 

解决办法是修改Linux内核参数,

修改系统socket最大链接数,在文件/etc/security/limits.conf最后加入下面两行:

* soft nofile 32768

* hard nofile 32768

或者缩小2MSL的时长、容许重用处于TIME_WAIT状态的TCP链接、快速回收处于 TIME_WAIT状态的TCP链接,修改/etc/sysctl.conf,添加以下几行:

 

#改系統默认的TIMEOUT时间
net.ipv4.tcp_fin_timeout=2

#启重用,容许将TIME_WAIT sockets从新用于新的TCP链接 默认为0表示关闭
net.ipv4.tcp_tw_reuse=1

#开启TCP链接中TIME_WAIT sockets的快速回收 默认为表示关闭
net.ipv4.tcp_tw_recycle=1

 

对于windows环境,可经过修改注册表进行配置:

\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters

添加一个DWORD类型的值TcpTimedWaitDelay,值能够根据实际状况配置。

\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters

添加一个DWORD类型的值MaxUserPort ,值能够根据实际状况配置。

 

上面这些参数根据实际状况进行配置。

 

从LVS 层面上看,调度算法不合理,致使请求过多分配到某一台服务器上

 

解决办法,根据实际状况指定合理的负载均衡解决方案。

 

从安全层面上看,当服务器遭到DDoS(拒绝服务攻击)时,服务器大量积压TIME_WAIT状态的TCP链接而没法向外提供服务

 

解决办法,增强安全防御。

相关文章
相关标签/搜索