
設定:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
Apache は AJP を使用してリクエストを転送しています。
問題:
一定期間が経過すると (一定ではなく、1 時間から 2 時間、または 1 日以上)、Tomcat は停止します。応答が停止するか、一般的な「サービスが一時的に利用できません」というメッセージが表示されます。
診断:
同じ設定のサーバーが 2 つあります。1 つはトラフィックの多い Web サイト (1 秒あたり数件のリクエスト) をホストし、もう 1 つはトラフィックの少ない Web サイト (数分ごとに数件のリクエスト) をホストします。両方の Web サイトは完全に異なるコードベースですが、同様の問題が発生します。
最初のサーバーでは、問題が発生すると、すべてのスレッドが徐々に占有され始め、制限 (MaxThreads 200) に達します。その時点で、サーバーは応答しなくなります (そして、長い時間が経過すると、サービスが利用できないページが表示されます)。
2 番目のサーバーでは、問題が発生するとリクエストに長い時間がかかり、完了するとサービスが利用できないページだけが表示されます。
MaxThreads の問題に関する言及以外に、Tomcat ログには、この問題の原因となる可能性のある特定の問題は示されていません。
ただし、Apache ログには AJP に関するランダムなメッセージが表示されています。以下は、表示されるランダムなメッセージの例です (順不同)。
[error] (70007)The timeout specified has expired: ajp_ilink_receive() can't receive header
[error] (104)Connection reset by peer: ajp_ilink_receive() can't receive header
[error] proxy: AJP: disabled connection for (localhost)
[error] ajp_read_header: ajp_ilink_receive failed
[error] (120006)APR does not understand this error code: proxy: read response failed from 127.0.0.1:8009 (localhost)
[error] ap_proxy_connect_backend disabling worker for (localhost)
トラフィックの多いサーバーで気づいたもう 1 つの奇妙な点は、問題が発生し始める直前に、データベース クエリにかかる時間が以前よりも大幅に長くなっていることです (通常は 5 ~ 50 ミリ秒ですが、2000 ~ 5000 ミリ秒)。これは、MaxThreads メッセージが表示されるまで 2 ~ 4 秒しか続きません。これは、サーバーが突然大量のデータ/トラフィック/スレッドを処理するようになった結果であると考えられます。
背景情報:
これら 2 台のサーバーは、かなり長い間問題なく稼働していました。当時、システムはそれぞれ 2 つの NIC を使用してセットアップされていました。内部トラフィックと外部トラフィックは分離されていました。ネットワークのアップグレード後、これらのサーバーを単一の NIC に移動しました (セキュリティと簡素化の理由から、これが推奨されました)。その変更後、サーバーでこれらの問題が発生し始めました。
解決:
明らかな解決策は、2 つの NIC の設定に戻すことです。その場合の問題は、ネットワーク設定が複雑になり、問題を無視しているように見えることです。1 つの NIC 設定で実行できるようにすることをお勧めします。
さまざまなエラー メッセージを Google で検索しても、役に立つ情報は何も得られませんでした (古い解決策か、問題とは無関係な解決策でした)。
さまざまなタイムアウトを調整してみましたが、サーバーが停止するまでの実行時間がわずかに長くなってしまっただけでした。
問題をさらに詳しく診断するにはどこを調べればよいかわかりません。問題が何であるかをまだ手探りで調べています。
1) AJP と Tomcat の設定が正しくないか、古くなっています (既知のバグなど)。2
) ネットワーク設定 (2 つの NIC と 1 つの NIC) が混乱やスループットの問題を引き起こしています。3
) Web サイト自体 (共通コードや使用されているプラットフォームはなく、サーブレットと JSP を使用した基本的な Java コードのみです)
アップデート1:
David Pashley の有益なアドバイスに従って、問題が発生している間にスタック トレース/スレッド ダンプを実行しました。その結果、200 個のスレッドすべてが次のいずれかの状態にあることがわかりました。
"TP-Processor200" daemon prio=1 tid=0x73a4dbf0 nid=0x70dd waiting for monitor entry [0x6d3ef000..0x6d3efeb0]
at oracle.jdbc.pool.OracleConnectionCacheImpl.getActiveSize(OracleConnectionCacheImpl.java:988)
- waiting to lock <0x7e3455a0> (a oracle.jdbc.pool.OracleConnectionCacheImpl)
[further stack trace removed for brevity]
"TP-Processor3" daemon prio=1 tid=0x08f142a8 nid=0x652a waiting for monitor entry [0x75c7d000..0x75c7ddb0]
at oracle.jdbc.pool.OracleConnectionCacheImpl.getConnection(OracleConnectionCacheImpl.java:268)
- waiting to lock <0x7e3455a0> (a oracle.jdbc.pool.OracleConnectionCacheImpl)
[further stack trace removed for brevity]
興味深いことに、200 個のスレッドのうち 1 つのスレッドだけがこの状態でした。
"TP-Processor2" daemon prio=1 tid=0x08f135a8 nid=0x6529 runnable [0x75cfe000..0x75cfef30]
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at oracle.net.ns.Packet.receive(Unknown Source)
at oracle.net.ns.DataPacket.receive(Unknown Source)
at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
[further stack trace removed for brevity]
このスレッドの Oracle ドライバーが、他のすべてのスレッドに完了するまで待機させている可能性があります。何らかの理由で、この読み取り状態のままになっているはずです (サーバーは自動的に回復することはなく、再起動が必要です)。
これは、サーバーとデータベース間のネットワーク、またはデータベース自体に関連している必要があることを示しています。診断作業は継続中ですが、ヒントがあれば助かります。
答え1
このバージョンの Oracle ドライバ (classes12 - かなり古い) には、デッドロックを引き起こすさまざまなバグが含まれていたことが判明しました (上記の TP-Processor2 の状態で確認できます)。新しい環境に切り替えるまで、この問題はアクティブになりませんでした。最新バージョン (ojdbc14) にアップグレードすると、プライマリ サーバーの問題が解決しました。
答え2
説明から判断すると、データベース クエリに時間がかかりすぎることが問題の原因であると考えられます。クエリに時間がかかる場合、リクエストにも時間がかかるため、一度に実行されるリクエストの数が増えます。ご覧のとおり、Tomcat スレッドが不足しています。データベースの問題を解決すれば、問題は解決するはずです。
- jstack または kill -3 $process_id を使用してスタック トレースを取得します。スレッドが停止したときに何を実行しているかを確認します。スレッドがすべてデータベースを待機している場合、それは私の理論を示す良い指標です。スレッドはすべて何らかのロックを待機している可能性があります。
- LambdaProbe をインストールします。これは、Tomcat が何をしているかを調べるのに非常に役立ちます。
- Tomcat をアップグレードしてください。5.5.8 は非常に古いです。現在は 5.5.27 になっていると思います。
答え3
/etc/tomcat7/server.xml にある AJP コネクタに connectionTimeout と keepAliveTimeout を追加します。
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"
connectionTimeout="10000" keepAliveTimeout="10000" />
AJPコネクタに関する情報参考:
connectionTimeout = このコネクタが接続を受け入れた後、要求 URI 行が表示されるまで待機する時間 (ミリ秒)。AJP プロトコル コネクタのデフォルト値は -1 (つまり無限) です。
keepAliveTimeout = このコネクタが接続を閉じる前に別の AJP 要求を待機するミリ秒数。デフォルト値は、connectionTimeout 属性に設定された値を使用することです。
connectionTimeout および keepAliveTimeout の値が定義されていない場合、AJP 接続は無期限に維持されます。スレッドが多くなるため、デフォルトの最大スレッド数は 200 です。
Lambda Probe からフォークされた、Apache Tomcat 用の高度なマネージャーおよびモニターである psi-probe をインストールすることをお勧めします。https://code.google.com/p/psi-probe/
答え4
安定性の点では、mod_ajp ではなく mod_proxy を使用した方がよい結果が得られたので、この解決策を試してください。これは非侵襲的であり、最良の場合、問題を解決しますが、最悪の場合、mod_ajp を排除することになります。
それ以外では、Tomcat が応答を停止し、すべてのリクエスト スレッドが停止しているようです。開発チームに何が起こっているか調べてもらいましょう。スレッドダンプを取るそれを彼らに届けることは役に立つでしょう。