现象
进程在,curl请求没反应,判定为进程假死
分析
查看TCP连接
[root@ip-XXXXbackup]# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 84
CLOSE_WAIT 8192
ESTABLISHED 141
SYN_RECV 1
LAST_ACK 1
`
dump问题线程的堆栈
➜ Commands pwd
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands
➜ Commands ./jvisualvm
RUNNABLE线程表示正在运行的线程
查看代码,发现这个接口,做的工作是去aliyun拿签名信息,然后返回。使用阿里云提供的sdk。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>3.2.6</version>
</dependency>
查看日志中,这条请求的时间为5个小时前的。对比dump的时间,和RUNNABLE,说明这个请求的线程一直处于运行状态,没有终止。
继续看源代码,接口做的工作,就是用阿里云的SDK接口去拿签名信息,拿到后返回给客户端。理论上,阿里云的请求不可能这么慢,即使慢,应该也要超时才对。阿里云的SDK封装的httpclient,httpclient是可以设置超时时间的。下面是SDK的超时时间:
图四,设置了连接超时和读取超时再看业务代码,并没有设置超时时间:
IClientProfile profile = DefaultProfile.getProfile(region, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
// 创建一个 AssumeRoleRequest 并设置请求参数
final AssumeRoleRequest request = new AssumeRoleRequest();
request.setVersion(version);
request.setMethod(MethodType.POST);
request.setProtocol(protocolType);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
request.setPolicy(policy);
if(expireTime > 0L) {
request.setDurationSeconds(expireTime);
}
final AssumeRoleResponse response = client.getAcsResponse(request);
return response;
所以如果阿里云出现问题,可能导致请求一只挂起,线程一只处于运行状态。
那为什么挂起请求,会在服务器出现大量的CLOSE_WAIT, 那需要从TCP的四次挥手说明。
TCP四次挥手
为什么需要四次挥手
以下内容来自google:
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。
这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。
收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。
首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1) TCP客户端发送一个FIN,用来关闭客户到服务器的[数据传送]
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3) 服务器关闭客户端的连接,发送一个FIN给客户端。
(4) 客户端发回ACK[报文]确认,并将确认序号设置为收到序号加1。
图五,TCP四次挥手
随便发一张TCP三次握手
图六,TCP三次握手大量CLOSE_WAIT的原因
客户端调用服务端的接口,读取超时时间为8s,8s后,如果客户端将断开连接也就是图五中,客户端发送的FIN。 服务端收到FIN后,变成CLOSE_WAIT。
因为服务端又发起了对阿里云的请求,送上面的业务代码得知。因为没有设置超时时间,导致服务端线程一直RUNABLE。
当客户端大量访问该接口时,就会出现大量CLOSE_WAIT。
为什么进程的CLOSE_WAIT数量为8192
出现假死时,CLOSE_WAIT数量为8192,表现的现象责任,应用服务器进程拒绝服务。
tomcat的最大连接数
tomcat的最大连接数参数是maxConnections,这个值表示最多可以有多少个socket连接到tomcat上。BIO模式下默认最大连接数是它的最大线程数(缺省是200),NIO模式下默认是10000,APR模式则是8192(windows上则是低于或等于maxConnections的1024的倍数)。如果设置为-1则表示不限制。
springboot升级到最新的2.0版本,默认开启的APR, 可以设置如下参数
server.tomcat.max-connections=
server.tomcat.accept-count=
网友评论