
私は MySQL に対してクエリを実行する Web アプリケーション (Tomcat/Hibernate/DBCP 1.4) を持っていますが、これは一定の負荷、たとえば 1 秒あたり 50 クエリでは正常に動作します。同じ中程度の負荷を HAProxy 経由でルーティングすると (依然として単一のデータベースのみを使用)、おそらく 500 クエリごとに 1 つのエラーが発生します。私のアプリケーションは次のように報告します:
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 プール サイズも高くなっています。
一般的に、JDBC プールと HAProxy プールを一緒に使用しても問題ありませんか? これまでにこのような問題に遭遇した人はいますか?
これを解決する方法については、すべてのクエリの前に「検証クエリ」を送信するというアイデアがあります。しかし、そこには一定のオーバーヘッドがあり、Web アプリケーションが MySQL に直接アクセスすると成功するのに対し、HAProxy を経由すると接続が切断される理由を知りたいです。
さらにデバッグして、「cD」以外の情報を取得するにはどうすればよいでしょうか? HAProxy をデバッグ モードで実行してみましたが、それ以上は何も表示されないようです。
更新 - 2013 年 1 月 4 日金曜日 11:49:28 ICT (JimB への返信)
show sess
haproxyから現在よりも多くの情報を取得する唯一の方法は、またはshow 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 秒ですが、timeBetweenEvictionRunsMillis
デフォルトでは 5 秒なので最大 65 秒になる可能性があります。基本的に、プール内のこれらのアイドル接続を考慮して、haproxy のタイムアウトが十分に高いことを確認する必要があります。
を 75 秒に増やしたtimeout client
ところ、上記のエラーが以前よりも発生しなくなったようです。
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 秒に設定されています。
testWhileIdle
もう 1 つのアプローチは、 を使用し、 接続をアクティブに保つことですvalildationQuery
。数秒ごとにいくつかのトラフィック パケットを送信すると、おそらく問題も軽減されるでしょう。
他に方法がない場合、開発者はこれらのオプションを試すことをお勧めします。
答え1
haproxy から現在よりも多くの情報を取得する唯一の方法は、show sess
またはshow sess <id>
コマンドを定期的に使用して各 TCP 接続の状態を監視することですが、さらに有用な情報が得られるかどうかはわかりません。
終了cD
状態は、最も役立つ情報です。正確には、クライアントとの確立された接続がタイムアウトしたことを意味します。これは、haproxy で、timeout client
構成内のパラメータを介して制御され、グローバルに設定されるか、フロントエンドまたは listen セクションで設定されます。
同時接続数が 7 を超えることはないとおっしゃっていますが、このログ エントリには、接続数が 3 しかないときに障害が発生したことが示されているため、接続制限の問題があるとは思えません (haproxy の制御外であっても)。
つまり、プールが時々新しい接続を追加し、それがいくつかのクエリを処理し、その後アイドル状態になるという現象が起きているようです。その接続がtimeout client
haproxy の設定よりも長くアイドル状態になると、haproxy は接続自体を終了します。次にその接続がプールから使用されると、上記のエラーが発生します。
Haproxy のデフォルトのタイムアウトは 10 秒です (サンプル構成では 50 秒だと思います)。私は JDBC にあまり詳しくありませんが、Tomcat のドキュメントによると、minEvictableIdleTimeMillis
プールからアイドル接続を削除する設定があり、デフォルトは 60 秒ですが、timeBetweenEvictionRunsMillis
デフォルトでは 5 秒なので最大 65 秒になる可能性があります。基本的に、プール内のこれらのアイドル接続を考慮して、haproxy のタイムアウトが十分に高いことを確認する必要があります。
testWhileIdle
もう 1 つのアプローチは、 を使用し、接続をアクティブに保つことですvalildationQuery
。数秒ごとにいくつかのトラフィック パケットを送信すると、おそらく問題も軽減されるでしょう。
[編集] @quanta の追加情報に対する返答:
haproxy タイムアウトが 75 秒になったとしても、セッション タイムアウトは確実に発生します。JDBC 接続の存続期間全体にわたって、私が知らない追加の要因がある可能性があります。このタイプのサービスに必要な接続は非常に少ないため、タイムアウトを 1 時間以上の非常に長い値に増やしても問題ありません。JDBC プールが古い接続を解放するのに本当に問題がある場合、これは問題を隠しているだけですが、簡単に修正できる可能性もあります。