HAProxy,從 JDBC 池連線時客戶端逾時

HAProxy,從 JDBC 池連線時客戶端逾時

我有一個針對 MySQL 執行查詢的 Web 應用程式 (Tomcat/Hibernate/DBCP 1.4),這對於特定負載(例如每秒 50 個查詢)來說效果很好。當我透過 HAProxy 路由相同的中等負載時(仍然只使用單一資料庫),我會遇到失敗,也許每 500 個查詢就會失敗一次。我的應用程式報告:

Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 196,898 milliseconds ago.    The last packet sent successfully to the server was 0 milliseconds ago.
at sun.reflect.GeneratedConstructorAccessor210.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1117)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3567)
...
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3017)
...

同時,HAProxy 日誌顯示大量條目,例如:

27] mysql mysql/db03 0/0/34605 2364382 cD 3/3/3/3/0 0/0
Oct 15 15:43:12 localhost haproxy[3141]: 127.0.0.1:35500 [15/Oct/2012:15:42:50.0

“cD”顯然表示客戶端超時的狀態。因此,雖然我的 web 應用程式說 HAProxy 拒絕接受新連接,但 HAProxy 卻說我的 web 應用程式不接受返回的資料。

我不包括我的 HAProxy 配置,因為我嘗試了許多不同的參數值,但結果基本上相同。特別是,我在全域部分和伺服器部分都將 maxconn 設定為高值和低值,統計資料中經常發生的情況是最大會話數上升到不超過 7 左右。

通常可以一起使用 JDBC 池和 HAProxy 池嗎?人們以前有遇到這樣的問題嗎?

我有一個關於如何解決這個問題的想法,那就是在每個查詢之前發送一個「驗證查詢」。但那裡有一定的開銷,我仍然想知道為什麼我的 web 應用程式在直接連接到 MySQL 時成功,但在透過 HAProxy 時連接丟失。

我怎樣才能進一步調試並獲得比“cD”更多的信息?我嘗試在調試模式下運行 HAProxy,但它似乎沒有顯示更多內容。


更新 - 1 月 4 日星期五 11:49:28 ICT 2013(回覆 JimB)

從 haproxy 獲取更多資訊的唯一方法是定期使用show sessorshow sess <id>命令來觀察每個 tcp 連線的狀態

以下是有關會議的一些資訊:

0x31f4310: proto=tcpv4 src=192.168.3.40:60401 fe=FE_mysql be=BE_mysql srv=mysql3 ts=08 age=1m2s calls=2 rq[f=909202h,l=0,an=00h,rx=13s,wx=,ax=] rp[f=109202h,l=0,an=00h,rx=13s,wx=,ax=] s0=[7,18h,fd=0,ex=] s1=[7,18h,fd=1,ex=] exp=13s

0x31fca50: proto=tcpv4 src=192.168.3.40:60423 fe=FE_mysql be=BE_mysql srv=mysql1 ts=08 age=2s calls=2 rq[f=909202h,l=0,an=00h,rx=1m13s,wx=,ax=] rp[f=109202h,l=0,an=00h,rx=1m13s,wx=,ax=] s0=[7,18h,fd=9,ex=] s1=[7,18h,fd=12,ex=] exp=1m13s

Haproxy 的預設逾時為 10 秒(我認為範例配置有 50 秒)。我對 JDBC 不太熟悉,但是從 Tomcat 的文檔來看,有一個設置minEvictableIdleTimeMillis,它將從池中驅逐空閒連接,預設為 60 秒,並且可能最多為 65 秒,因為預設 timeBetweenEvictionRunsMillis為 5 秒。基本上,您需要確保 haproxy 逾時足夠高,以考慮池中的這些空閒連接。

我已將時間增加到timeout client75 秒,現在上述錯誤發生的次數似乎比以前少了:

2013-01-04 11:59:59 調試:通訊鏈路故障

最後一次從伺服器成功接收的封包是在 145,255 毫秒前。最後一次成功發送到伺服器的資料包是在 10 毫秒前。

我還想指出的是:除了上述之外,還有一些這樣的錯誤:

通訊鏈路故障 最後一次成功發送到伺服器的資料包是在 0 毫秒前。驅動程式尚未收到來自伺服器的任何資料包。

在伺服器端,有時我會看到sD斷開連接時的標誌:

haproxy[15770]: 192.168.3.40:56944 [04/Jan/2013:11:06:55.895] FE_mysql BE_mysql/mysql1 0/0/77153 1954480 sD 1/1/1/1/0 0/0

也設定timeout server為 75 秒。

另一種方法是使用testWhileIdlevalildationQuery保持連線處於活動狀態,因為每隔幾秒鐘發送幾個流量資料包也可能會緩解該問題。

如果沒有其他方法,我建議開發人員嘗試這些選項。

答案1

從 haproxy 獲取比您擁有的更多資訊的唯一方法是定期使用show sessorshow sess <id>命令來觀察每個 tcp 連接的狀態,儘管我不確定您是否會獲得任何更有用的信息。

終止狀態cD是您擁有的最有用的信息。它的確切意義是與客戶端建立的連線逾時。這是透過timeout client配置中的參數在 haproxy 中控制的,全域設置,或在 frontent 或 Listen 部分。

你說你沒有看到並發連接超過7個,並且這個日誌條目顯示只有3個連接時發生故障,所以我懷疑你有連接限制問題(即使在haproxy的控制範圍之外)。

所以看起來正在發生的情況是,池偶爾會添加一個新連接,該連接處理一些查詢,然後閒置。當此連線空閒時間超過timeout clienthaproxy 中的設定時,haproxy 將自行終止連線。下次從池中使用該連線時,您會收到上述錯誤。

Haproxy 的預設逾時為 10 秒(我認為範例配置有 50 秒)。我對 JDBC 不太熟悉,但是從 Tomcat 的文檔來看,有一個設置minEvictableIdleTimeMillis,它將從池中驅逐空閒連接,預設為 60 秒,並且可能最多為 65 秒,因為預設timeBetweenEvictionRunsMillis為 5 秒。基本上,您需要確保 haproxy 逾時足夠高,以考慮池中的這些空閒連接。

另一種方法是使用testWhileIdlevalildationQuery保持連線處於活動狀態,因為每隔幾秒鐘發送幾個流量資料包也可能會緩解該問題。

[編輯] 回應@quanta的附加資訊:

儘管 haproxy 逾時現在是 75 秒,但您肯定仍然會遇到會話逾時。 JDBC 連線的總生命週期中可能存在一些我不知道的附加作用。由於此類服務所需的連接很少,因此將超時增加到極高(大約一個小時或更長時間)也沒有什麼問題。如果 JDBC 池確實在釋放舊連線時出現問題,這只會掩蓋問題,但也可能是個簡單的修復。

相關內容