問題:
Cannot send, channel has already failed: tcp://ip:61616
javax.jms.JMSException: Cannot send, channel has already failed: tcp://ip:61616
應用連不上mq
解決方案:
一,分析思路:
1.現象:通過netstat 查看與61616相關的連接狀況,發現130多個CLOSE_WAIT
2.是什麼原因造成這麼多的CLOSE_WAIT?
2.1 主要原因是某種情況下應用關閉了socket連接,但是mq忙於讀或者寫,沒有關閉連接.
2.2 代碼需要判斷socket,一旦讀到0,斷開連接,read返回負,檢查一下errno,如果不是AGAIN,就斷開連接。
3.造成CLOSE_WAIT之後服務爲啥連不上mq呢?
linux分配給一個用戶的文件句柄是有限的,CLOSE_WAIT狀態一直被保持,意味着對應數目的通道就一直被佔着,一旦達到句柄數上線,新的請求就無法被處理了,應用程序可能會返回大量的Too many openfiles異常.
4.什麼是CLOSE_WAIT?
4.1 mq爲被連接端,java服務爲主動方,在被動關閉情況下,mq已經接收到FIN,但是還沒有發送自己的FIN的時刻,連接處於CLOSE_WAIT狀態;
二,解決辦法:
1.重啓mq
2,linux下設置如下三個參數:
/proc/sys/net/ipv4/tcp_keepalive_time當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時。
/proc/sys/net/ipv4/tcp_keepalive_intvl當探測沒有確認時,重新發送探測的頻度。缺省是75秒。
/proc/sys/net/ipv4/tcp_keepalive_probes在認定連接失效之前,發送多少個TCP的keepalive探測包。缺省值是9。這個值乘以tcp_keepalive_intvl之後決定了,一個連接發送了keepalive之後可以有多少時間沒有迴應。
三.瞭解擴展
1.客戶端先發送FIN,進入FIN_WAIT1狀態
服務端收到FIN,發送ACK,進入CLOSE_WAIT狀態,客戶端收到這個ACK,進入FIN_WAIT2狀態
服務端發送FIN,進入LAST_ACK狀態
客戶端收到FIN,發送ACK,進入TIME_WAIT狀態,服務端收到ACK,進入CLOSE狀態
客戶端TIME_WAIT持續2倍MSL時長,在linux體系中大概是60s,轉換成CLOSE狀態
2.服務端使用的短鏈接,每次客戶端請求後,服務端都會主動發送FIN關閉連接.最後進入到time_wait狀態.對於訪問量大的web server,會存在大量的TIME_WAIT狀態.讓服務器能夠快速回收和重用那些TIME_WAIT的資源,可修改內核參數.
修改/etc/sysctl.conf如下:
#對於一個新建連接,內核要發送多少個 SYN 連接請求才決定放棄,不應該大於255,默認值是5,對應於180秒左右時間
net.ipv4.tcp_syn_retries=2
#表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時,改爲300秒
net.ipv4.tcp_keepalive_time=1200
net.ipv4.tcp_orphan_retries=3
#表示如果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間
net.ipv4.tcp_fin_timeout=30
#表示SYN隊列的長度,默認爲1024,加大隊列長度爲8192,可以容納更多等待連接的網絡連接數。
net.ipv4.tcp_max_syn_backlog = 4096
#表示開啓SYN Cookies。當出現SYN等待隊列溢出時,啓用cookies來處理,可防範少量SYN***,默認爲0,表示關閉
net.ipv4.tcp_syncookies = 1
#表示開啓重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認爲0,表示關閉
net.ipv4.tcp_tw_reuse = 1
#表示開啓TCP連接中TIME-WAIT sockets的快速回收,默認爲0,表示關閉
net.ipv4.tcp_tw_recycle = 1
##減少超時前的探測次數
net.ipv4.tcp_keepalive_probes=5
##優化網絡設備接收隊列
net.core.netdev_max_backlog=3000
修改完之後執行/sbin/sysctl -p讓參數生效。