linux上TCP connection timeout問題解決辦法
linux上TCP connection timeout問題解決辦法
最近在產(chǎn)線上經(jīng)常出現(xiàn)connection timeout的問題,先看看Java 中關(guān)于connection timeout 的異常如何產(chǎn)生
JAVA中的timeout
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)
我們能經(jīng)??吹降腸onnect timed out異常產(chǎn)生,看一下java 是如何生成這個(gè)異常
plainsocketimpl.c 中
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的時(shí)候,是調(diào)用 NET_Poll 或者 NET_Select, 在linux 上就是使用 poll/select
當(dāng)發(fā)生timeout的時(shí)候connect_rv=0 ,這里有個(gè)注意點(diǎn)雖然在poll/select 是傳入timeout的時(shí)間,但是這是會(huì)被打斷的,connect_rv返回的值為-1 ,所以jvm里面重新計(jì)算了timeout , 確保timeout 的時(shí)間片已經(jīng)運(yùn)行完了,才推出循環(huán)。
newTime = JVM_CurrentTimeMillis(env, 0);
timeout -= (newTime - prevTime);
if (timeout <= 0) {
connect_rv = 0;
break;
}
同時(shí)設(shè)置connect_rv 為0, 也是下面只有當(dāng)connect_rv為0的時(shí)候才拋出connect timeout
什么是connect timeout ?
也就是client 發(fā)出 syn 包,server端在你指定的時(shí)間內(nèi)沒有回復(fù)ack,poll/select 返回0
server 端為什么沒有回復(fù)ack, 因?yàn)閟yn包的回復(fù)是內(nèi)核層的,要么網(wǎng)絡(luò)層丟包,要么就是內(nèi)核層back_log的queue滿了,關(guān)于backlog在本片中就不詳細(xì)描述了。
當(dāng)時(shí)查看產(chǎn)線上的連接最高能到1000多,同時(shí)查看了backlog 的queue的大小
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
有8192 在產(chǎn)線上沒有這么多的客戶端的連接,不可能backlog queue會(huì)滿,雖然syn_backlog 的設(shè)置是8192 但并不代表服務(wù)器啟動(dòng)的時(shí)候設(shè)置成了8192,所以必須查這個(gè)端口所設(shè)置的backlog大小
ss -lt
看到Send-Q在8080端口是128 ,原來在服務(wù)器端啟動(dòng)listen 的時(shí)候設(shè)置了128的backlog
查看tomcat 的配置,默認(rèn)bio的設(shè)置
<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"
/>
產(chǎn)線上已經(jīng)設(shè)置了acceptCount, 默認(rèn)是100 但是這里設(shè)置了是5000 ,這與通過ss看到的send-q的結(jié)果嚴(yán)重不符合
通過內(nèi)核代碼分析,發(fā)現(xiàn)原來內(nèi)核參數(shù)不僅僅是通過tcp_max_syn_backlog控制,同時(shí)也受somaxconn控制
查看
cat /proc/sys/net/core/somaxconn
發(fā)現(xiàn)值是128, OK 原因找到了,修改/etc/sysctl.conf 添加
net.core.somaxconn = 8192
sysctl -f /etc/sysctl.conf 重新加載一下,這樣就能改變?nèi)至?/p>
問題:是1000多個(gè)連接,500個(gè)工作線程,因?yàn)閎acklog的大小是受socket.accept控制的,我們通常境況下會(huì)單獨(dú)起一個(gè)線程去serversocket.accept(),而當(dāng)前server的load并不高,不因該會(huì)出現(xiàn)back_log queue出現(xiàn)滿的情況,更何況只有1000多個(gè)連接,代碼就是真相,查看tomcat的源碼。
原來accptor 線程在accept 之前,會(huì)去countUpOrWaitConnection 發(fā)現(xiàn)接受到的的socket數(shù)目大于設(shè)置的work線程數(shù)目的時(shí)候,會(huì)停止accept.
<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;
}
也就是說當(dāng)并發(fā)超過628個(gè)連接以上,就有可能出現(xiàn)backlog queue滿的情況,而出現(xiàn)connect timeout的情況,一切皆清楚了。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
你需要知道的16個(gè)Linux服務(wù)器監(jiān)控命令
如果你想知道你的服務(wù)器正在做干什么,你就需要了解一些基本的命令,一旦你精通了這些命令,那你就是一個(gè) 專業(yè)的 Linux 系統(tǒng)管理員2012-03-03
Linux下安裝MariaDB數(shù)據(jù)庫(kù)問題及解決方法(二進(jìn)制版本的安裝)
MariaDB數(shù)據(jù)庫(kù) 分為源代碼版本和二進(jìn)制版本,源代碼版本需要cmake編譯,這里是二進(jìn)制版本的安裝。下面通過本文給大家介紹Linux下安裝MariaDB數(shù)據(jù)庫(kù)問題及解決方法(二進(jìn)制版本的安裝),感興趣的朋友參考下吧2016-11-11
基于linux配置selenium環(huán)境并實(shí)現(xiàn)運(yùn)行
這篇文章主要介紹了基于linux配置selenium環(huán)境并實(shí)現(xiàn)運(yùn)行,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
關(guān)于安裝LNMP集成包后上傳圖片報(bào)500錯(cuò)誤的解決方法
這篇文章主要給大家介紹了關(guān)于安裝LNMP集成包后上傳圖片報(bào)500錯(cuò)誤的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01

