セットアップ:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
ApacheはAJPを使用してリクエストを転送しています。
問題:
一定の期間が経過すると(一定ではない場合、1〜2時間、または1日以上の場合があります)、Tomcatが停止します。応答を停止するか、一般的な「Service Temporarily Unavailable」を表示します。
診断:
同じ設定の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つの奇妙な点は、問題が発生し始める直前に、データベースクエリが以前よりもはるかに長くかかっていることです(2000〜5000ミリ秒に対して通常5〜50ミリ秒)。これは、MaxThreadsメッセージが表示される前に2〜4秒間だけ続きます。これは、サーバーが突然大量のデータ/トラフィック/スレッドを処理した結果であると想定しています。
背景情報:
これらの2台のサーバーは、かなり前から問題なく実行されていました。その間、システムはそれぞれ2つのNICを使用してセットアップされました。彼らは内部と外部のトラフィックを分離しました。ネットワークのアップグレード後、これらのサーバーを単一のNICに移動しました(これは、セキュリティ/シンプルさの理由から推奨されました)。その変更後、サーバーはこれらの問題を抱え始めました。
解像度:
明白な解決策は、2つのNICのセットアップに戻ることです。それに関する問題は、それがネットワーク設定でいくつかの複雑さを引き起こすであろうということです、そしてそれは問題を無視しているようです。単一のNICセットアップで実行してみてください。
さまざまなエラーメッセージをググリングしても、有用なものは何もありませんでした(古い解決策または問題とは無関係)。
さまざまなタイムアウトを調整してみましたが、これにより、サーバーが停止するまでに少し時間がかかりました。
問題をさらに診断するためにどこを見ればよいかわかりません。私たちはまだ問題が何であるかをストローで把握しています:
1)AJPとTomcatの設定が正しくないか、古い(つまり、既知のバグか?)
2)ネットワーク設定(2つのNICと1つのNIC)が混乱またはスループットの問題を引き起こしています。
3)Webサイト自体(一般的なコードはなく、プラットフォームも使用されていません。基本的なJavaサーブレットとJSPを使用したコード)
更新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ドライバが、他のすべてのスレッドに、それが完了するのを待機させている可能性があります。何らかの理由で、この読み取り状態のままになっている必要があります(サーバーが単独で回復することはなく、再起動が必要です)。
これは、サーバーとデータベース間のネットワーク、またはデータベース自体に関連している必要があることを示唆しています。診断作業は継続していますが、ヒントがあれば参考になります。
このバージョン(classes12-かなり古い)のOracleドライバーには、デッドロックを引き起こすさまざまなバグが含まれていることがわかります(上記のTP-Processor2の状態を参照)。新しい環境に切り替えるまでアクティブになりませんでした。最新バージョン(ojdbc14)にアップグレードすると、プライマリサーバーの問題が解決しました。
説明から、問題はデータベースクエリに時間がかかりすぎていることが原因である可能性があることをお勧めします。クエリに時間がかかる場合は、リクエストに時間がかかるため、一度に実行するクエリが多くなります。ご覧のとおり、Tomcatスレッドが不足しています。データベースの問題を解決したら、大丈夫です。
/etc/Tomcat7/server.xmlにあるAJPコネクターにconnectionTimeoutおよびkeepAliveTimeoutを追加します。
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"
connectionTimeout="10000" keepAliveTimeout="10000" />
AJPコネクタに関する情報( )https://Tomcat.Apache.org/Tomcat-7.0-doc/config/ajp.html
connectionTimeout =このコネクターが接続を受け入れた後、要求URI行が表示されるのを待機するミリ秒数。 AJPプロトコルコネクタのデフォルト値は-1(無限)です。
keepAliveTimeout =このコネクターが接続を閉じる前に別のAJP要求を待機するミリ秒数。デフォルト値では、connectionTimeout属性に設定されている値が使用されます。
ConnectionTimeoutおよびkeepAliveTimeout値が定義されていない場合、AJP接続は無期限に維持されます。多くのスレッドが発生するため、デフォルトの最大スレッド数は200です。
Lambdaプローブから分岐した、Apache Tomcatの高度なマネージャーとモニターであるpsi-probeをインストールすることをお勧めします。 https://code.google.com/p/psi-probe/
AJPが機能する方法のため、Apache間の永続的な接続(mod_proxy_ajpまたはmod_jkのいずれかを使用)は、安全にのみ閉じることができますクライアントによって。この場合、クライアントはワーカープロセスの寿命の間、Tomcatへの接続を開いて保持するApacheワーカーです。
この動作のため、Tomcatワーカースレッドよりも多くのApacheワーカーを持つことはできません。これを行うと、追加のhttpワーカーがTomcatへの接続に失敗し(受け入れキューがいっぱいになるため)、バックエンドがDOWNとマークされます。
安定性の観点から、mod_ajpではなくmod_proxyを使用した方が良い結果が得られたので、その解決策を試してください。それは非侵襲的です-せいぜい問題を解決し、最悪の場合mod_ajpを除外します。
それ以外は、Tomcatが応答を停止し、すべてのリクエストスレッドが拘束されているように聞こえます。あなたの開発チームに何が起こっているのかを調べてもらいます- スレッドダンプを取る そしてそれを彼らに提供することは有用でしょう。
サーバーがしばらく稼働し、突然スローダウンしてサービス障害が発生し始めたと聞いて最初に思うのは、RAMが不足し、スワップがスラッシングしていることです。私は違います。発生しているAJP障害がタイムアウトの結果であるかどうかは明確ですが、それが完全に不合理に見えるわけではありません。ただし、NICに接続する明白な方法は確認しないでください。いずれにしても、これらのイベントが発生したときのメモリ使用量の状況の画像。
RAMが不足している場合は、Apache MaxClients
を減らし、ListenBacklog
を増やす必要があるかもしれません。
ちなみに、質問をよく整理して完全にしてくれてありがとう。
Redhat環境で、proxy_ajpとTomcatを使用して同様のログエラーが発生しました。 httpdパッケージを更新することで解決:
yum update httpd
から:
に:
次にApacheを再起動し、続いてTomcatを再起動しました。
これで解決しました!