linux上TCP connection timeout问题解决办法
最近在产线上经常出现connection timeout的问题,先看看Java 中关于connection timeout 的异常如何产生
JAVA中的timeout
1
2
3
4
5
6
7
8
|
java.net.SocketTimeoutException: connect timed out
客户端异常:connect timed out
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java: 345 )
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java: 206 )
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java: 188 )
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java: 392 )
at java.net.Socket.connect(Socket.java: 589 )
|
我们能经常看到的connect timed out异常产生,看一下java 是如何生成这个异常
plainsocketimpl.c 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
while (1) {
jlong newTime;
#ifndef USE_SELECT
{
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLOUT;
errno = 0;
connect_rv = NET_Poll(&pfd, 1, timeout);
}
#else
{
fd_set wr, ex;
struct timeval t;
t.tv_sec = timeout / 1000;
t.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&wr);
FD_SET(fd, &wr);
FD_ZERO(&ex);
FD_SET(fd, &ex);
errno = 0;
connect_rv = NET_Select(fd+1, 0, &wr, &ex, &t);
}
#endif
if (connect_rv >= 0) {
break ;
}
if (errno != EINTR) {
break ;
}
/*
* The poll was interrupted so adjust timeout and
* restart
*/
newTime = JVM_CurrentTimeMillis( env , 0);
timeout -= (newTime - prevTime);
if (timeout <= 0) {
connect_rv = 0;
break ;
}
prevTime = newTime;
} /* while */
if (connect_rv == 0) {
JNU_ThrowByName( env , JNU_JAVANETPKG "SocketTimeoutException" ,
"connect timed out" );
/*
* Timeout out but connection may still be established.
* At the high level it should be closed immediately but
* just in case we make the socket blocking again and
* shutdown input & output.
*/
SET_BLOCKING(fd);
JVM_SocketShutdown(fd, 2);
return ;
}
|
这里可以看到在做connect的时候,是调用 NET_Poll 或者 NET_Select, 在linux 上就是使用 poll/select
当发生timeout的时候connect_rv=0 ,这里有个注意点虽然在poll/select 是传入timeout的时间,但是这是会被打断的,connect_rv返回的值为-1 ,所以jvm里面重新计算了timeout , 确保timeout 的时间片已经运行完了,才推出循环。
1
2
3
4
5
6
|
newTime = JVM_CurrentTimeMillis(env, 0);
timeout -= (newTime - prevTime);
if (timeout <= 0) {
connect_rv = 0;
break ;
}
|
同时设置connect_rv 为0, 也是下面只有当connect_rv为0的时候才抛出connect timeout
什么是connect timeout ?
也就是client 发出 syn 包,server端在你指定的时间内没有回复ack,poll/select 返回0
server 端为什么没有回复ack, 因为syn包的回复是内核层的,要么网络层丢包,要么就是内核层back_log的queue满了,关于backlog在本片中就不详细描述了。
当时查看产线上的连接最高能到1000多,同时查看了backlog 的queue的大小
1
|
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
|
有8192 在产线上没有这么多的客户端的连接,不可能backlog queue会满,虽然syn_backlog 的设置是8192 但并不代表服务器启动的时候设置成了8192,所以必须查这个端口所设置的backlog大小
1
|
ss -lt
|
看到Send-Q在8080端口是128 ,原来在服务器端启动listen 的时候设置了128的backlog
查看tomcat 的配置,默认bio的设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<Connector executor= "tomcatThreadPool"
port= "8080"
protocol= "HTTP/1.1"
acceptCount= "5000"
connectionTimeout= "25000"
maxHttpHeaderSize= "8192"
useBodyEncodingForURI= "true"
enableLookups= "false"
redirectPort= "8443"
URIEncoding= "UTF-8"
maxThreads= "500"
maxKeepAliveRequests= "1000"
keepAliveTimeout= "30000"
/>
|
产线上已经设置了acceptCount, 默认是100 但是这里设置了是5000 ,这与通过ss看到的send-q的结果严重不符合
通过内核代码分析,发现原来内核参数不仅仅是通过tcp_max_syn_backlog控制,同时也受somaxconn控制
查看
1
|
cat /proc/sys/net/core/somaxconn
|
发现值是128, OK 原因找到了,修改/etc/sysctl.conf 添加
1
|
net.core.somaxconn = 8192
|
sysctl -f /etc/sysctl.conf 重新加载一下,这样就能改变全局了
问题:是1000多个连接,500个工作线程,因为backlog的大小是受socket.accept控制的,我们通常境况下会单独起一个线程去serversocket.accept(),而当前server的load并不高,不因该会出现back_log queue出现满的情况,更何况只有1000多个连接,代码就是真相,查看tomcat的源码。
原来accptor 线程在accept 之前,会去countUpOrWaitConnection 发现接受到的的socket数目大于设置的work线程数目的时候,会停止accept.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<strong>countUpOrAwaitConnection< /strong >();
Socket socket = null;
try {
// Accept the next incoming connection from the server
// socket
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
countDownConnection();
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
}
|
也就是说当并发超过628个连接以上,就有可能出现backlog queue满的情况,而出现connect timeout的情况,一切皆清楚了。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
原文链接:http://blog.csdn.net/raintungli/article/details/37879907