動作の仕組み
Tomcatのコネクタに要求が送られるたびに、カスタムパフォーマンス追跡Valveが呼び出されます。Valveは、プールされたリソースの使用率に関する情報をMBeanから収集し、それをログファイルに出力します。ログファイルには、タイムスタンプや、要求の種類に関するそれ以外の有用な情報も併せて記録されます。このログファイルは、リアルタイムでの監視に利用することも、後からサーバのパフォーマンスと容量を分析するために利用することもできます。
今回のソリューションでは、Tomcatのソースコードに付属するAccessLogValveに手を加えています。AccessLogValveは、Webロギングの標準に沿った形式のログファイルを生成するので、出力はApacheの標準ログと同じような外観になります。これらのログステートメントには、それ自体で非常に有用な情報が含まれるため、それらはそのまま保持しつつ、リソースプールの状態情報をログ出力に追加しています。もちろん、ログに記録する情報は各自の必要に応じて選んでかまいません。
次に、ソリューションの具体的な実装手順を示します。
- カスタムリソース追跡Valveを実装します。Tomcatには、この目的のために継承できる定義済みのベースValveが用意されています。場所は「catalina.jar」ファイル内です。このjarファイルをクラスパスに追加し、次のようにカスタムValveで継承します。
invokeメソッドの中で、次のようにリソースプールMBeanにアクセスしてパフォーマンス属性を読み取ります(詳細については、添付のソースコードとコメントを参照してください)。- Valveがアクセスされるたびに、次のようにパフォーマンス情報をログに記録します。
- Valveを登録します。次のステートメントを、Tomcatの「server.xml」ファイルの
</Host>終了タグの直前に挿入します。 - Valveをインストールします。私はカスタムValveをjarファイルとして展開し、Tomcatの「server/lib」ディレクトリ内に配置しました。このjarをサーバのクラスパスに追加すると、カスタム監視プロセスが利用するTomcatのすべての内部クラスにアクセスできるようになります。
- ログを解析および解釈します。アプリケーションに対して負荷テストとストレステストを実行すると、
ResourceTrackingAccessLogValveが、実行時のリソースの消費とプールされたリソースの使用率に関する貴重な情報を「localhost_resource_log.txt」ファイルに記録します。
public class ResourceTrackingAccessLogValve extends ValveBase implements Lifecycle {
markオブジェクトを追加するだけです)public void invoke(Request request, Response response) throws IOException, ServletException {
//Lazy instantiate MBeanServer using Tomcat's core // MbeanUtility class if ( this.mbeanServer == null ) { this.mbeanServer = MBeanUtils.createServer (); }//end if //Get the instance of the db pool MBean object // by fetching it through the ObjectName (connectionPoolMBeanID) if ( this.dbPoolObjectName == null ) { this.connectionPoolMBeanID.put( "name","\"" + this.getJdbcName() + "\"" ); this.connectionPoolMBeanID.put( "class", "javax.sql.DataSource" ); this.connectionPoolMBeanID.put ( "host", this.getHost() ); this.connectionPoolMBeanID.put( "path", this.getApplicationPath() ); this.connectionPoolMBeanID.put( "type", "DataSource" ); this.dbPoolObjectName = ObjectName.getInstance( this.domain , this.connectionPoolMBeanID ); } MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo ( this.dbPoolObjectName ); MBeanAttributeInfo attributeInfo[] = mBeanInfo.getAttributes(); for (int i = 0; i < attributeInfo.length; i++) { if ( attributeInfo[ i ].getName().equals( this.DB_POOL_NUM_ACTIVE_ATTRIBUTE ) ) { this.dbConnectionCount = mbeanServer.getAttribute( dbPoolObjectName, attributeInfo[ i ].getName() ).toString(); } if ( attributeInfo[ i ].getName().equals( this.DB_POOL_MAX_ACTIVE_ATTRIBUTE ) ) { this.dbMaxSize = mbeanServer.getAttribute( dbPoolObjectName, attributeInfo[ i ].getName() ).toString(); } }//end for
result.append( " | busy threads: " + this.threadsBusyCount ); result.append( " active threads: " + this.threadsCurrentCount ); result.append( " active db connections: " + this.dbConnectionCount ); result.append ( " max db connections : " + this.dbMaxSize );
127.0.0.1 [29/Aug/2006:12:58:05:265 -0400] 12 200 896 /Test/ GET - - - - | busy threads: 2 active connector threads: 51 active db connections: 8 max d b connections : 30
<Valve className=" tomcat.tracking.ResourceTrackingAccessLogValve"
directory="logs" prefix="localhost_resource_log." suffix=".txt"
connectorName="jk-8009"
domain="Catalina"
applicationPath="/Test"
pattern="%a %t %D %s %b %U %q %m" resolveHosts="false"/>
この情報は、運用設定作業に計り知れない価値をもたらします。スレッドの最大数を観察し、使用率の高いコネクタスレッドとアクティブなデータベース接続の間の比率に注目してください。この比率から、さまざまな負荷の下でアプリケーションがどのリソースを消費しているかだけでなく、アプリケーションが実際どの程度スケーラブルかという点まで、アプリケーションの状態に関する多くの事柄を知ることができます。スケーラブルなアプリケーションでは、Web接続の数が増加してもデータベース接続の使用率が控えめに推移する傾向があります。
それ以外の機能
本稿で説明したテクニックを用いると、高負荷の状況でのTomcatのリソース使用率を現実的に把握することができます。私の経験から言えば、スケーラブルで応答性に優れたアプリケーションを実行する際には、リソースプールなどの主要な設定パラメータを理解し、運用アプリケーションのリアルタイムの要件を把握することが不可欠です。ただし、ここで示した手法はリソース使用率を追跡する以外の目的にも利用できます。
添付のソースコードに含まれるValveを詳しく調べ、少し時間をかけてMBeanのアクセス方法を理解すると、おそらくセキュリティの追跡や純粋なパフォーマンス監視などの他の要件にも、この手法が役立つことが分かるでしょう。
JConsoleなどのツールを使ってTomcatの豊富なMBeanのAPIを探究し、サーバの内部やアプリケーションの実行時の動作に関する理解をさらに深めることも強くお勧めします。
