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