linux上TCP connection timeout问题解决办法

2025-05-27 0 96

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

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 linux上TCP connection timeout问题解决办法 https://www.kuaiidc.com/61208.html

相关文章

发表评论
暂无评论