japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2009年7月4日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2005年11月22日 18:00

簡単に拡張できる軽量なモニタリングフレームワークの構築

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート

はじめに

 開発者はたびたび、一連のアプリケーションのモニタリングを行い、障害発生時には復旧策を実行しなければならない、という状況に直面します。私の場合は、さまざまなOS上に分散した複数のコンポーネントから成るマルチレイヤの電子通信バックエンドシステム用のモニタリングサーバを設計する必要性に迫られました。要求分析を行った結果、わざわざJMXを使って解決するほどの事例ではないことがわかりました。そこで、拡張やカスタマイズが容易にできる軽量なJavaフレームワークを実装し、そのうえでモニタリングシステムを構築することにしたのです。

 本稿では、このフレームワークを使用して、データベース、アプリケーション、Webサーバのようなさまざまなバックエンドプロセスのモニタリングを行う方法と、プロセスの再起動、ディスク使用領域オーバー時のクリーンアップ、障害情報のレポートおよびログの生成のような修復処理を呼び出す方法を紹介します。本稿のダウンロードサンプルには、フレームワークのソースコードとサンプルクライアントが含まれています。

モニタリングコンポーネントの基本

 モニタリングの対象となるあらゆるコンポーネントに共通する属性を次に示します。

  1. コンポーネントの状態
  2. 「正常」「異常」「モニタリング不可」の3つのいずれかの値をとります。
  3. 修復処理
  4. プロセスがダウンした場合、この処理を実行することで回復が行われます。ただし、すべてのコンポーネントが障害から回復できるわけではありません。たとえば、回復不可能かつクリティカルなプロセスがダウンした場合にシステムにできるのは、アラームを発することくらいです。
  5. モニタリングの頻度
  6. どの程度の頻度でコンポーネントのモニタリングを行うかを表します。頻度カウンタは設定可能なパラメータです。コンポーネントの種類に応じて、秒単位または分単位で値を指定します。
  7. クリティカル性
  8. モニタリングされるコンポーネントのクリティカル性の高さを定めます。「高」「中」「低」の3つのレベルのいずれかに設定します。なお、モニタリングの頻度は低くてもクリティカル性の高いコンポーネントが存在しうることに注意してください。

フレームワークのもとで

 図1のクラス図に示すように、このフレームワークは4つのクラスと1つのインターフェイスで構成されます。このフレームワークを呼び出すクライアントコードには、InfoインターフェイスとLWMFrameworkクラスだけが公開されます。こうすることで、クライアント側はフレームワーク内部の実装を意識せずに済むわけです。

図1 フレームワークのクラス図
図1 フレームワークのクラス図

 モニタリングが必要なコンポーネントには、すべてこのInfoインターフェイスを実装しなければなりません。isOk()メソッドによってコンポーネントのステータスをチェックし、process()メソッドによって修復処理のハンドリングを行います。

public interface Info {
    //checks the status 
    //returns true if ok 
    //throws Exception if it could not be monitored
    public boolean isOk() throws Exception;
    //handles corrective action
    public boolean process();
}

 このフレームワークでは、次の2つのバックグラウンドスレッドが使われています。

  1. 各コンポーネントのステータスを確認するStatusMonitor
  2. 修復処理の実行とステータスの報告を行うStatusManager

 StatusMonitorクラス(リスト1参照)は、毎秒あるいは設定した時間間隔ごとにポーリングを行い、モニタリング可能なオブジェクトのカウンタ値が期限切れになっていないかを確認します。期限切れの場合は、StatusMonitorクラスはそのオブジェクトに対してmonitor()メソッドを実行します。

リスト1 StatusMonitorクラス
public class StatusMonitor extends Thread {
    private final int monInterval; //in milliseconds
    private final Monitorable[] monArray;
    public void run() {
        while(true) {
           for(int cnt = 0; cnt < Integer.MAX_VALUE;
               cnt++) {
               //iterate through the array
               for(int i = 0; i < monArray.length; i++) {
                   //check if counter expired
                   if((cnt % monArray[i].getCounter()) 
                       == 0) monArray[i].monitor();
               }
           }
           try { 
               sleep(monInterval);
           } catch(InterruptedException ie) {}
        }
    }
}

 モニタリング可能オブジェクトはMonitorableクラス(リスト2参照)で表されます。個々のモニタリング可能オブジェクトは、StatusManagerオブジェクトおよびInfoオブジェクトに関連付けられています。また、カウンタ(モニタリング頻度)やクリティカル性といった属性は、モニタリング可能オブジェクトそのもののメンバとして用意されています。

リスト2 Monitorableクラス
public class Monitorable {
    private int counter;
    private byte criticality;
    private byte prevStatus;
    private StatusManager manager;
    private Info info;
    public void monitor() {
        byte currentStatus = NOT_OK;
        try {
            if(info.isOk()) {
               currentStatus = Monitorable.OK;
            }
        } catch(Exception ex) { 
            currentStatus = 
               Monitorable.COULD_NOT_BE_MONITORED;
        }
        //call setchanged if there is change in status
        //or if status is not ok or cud not be monitored
        if((currentStatus != prevStatus) || 
                currentStatus != StatusManager.OK) {
            synchronized(this) {
                prevStatus = currentStatus;
            }
            manager.setChanged(this);
        }
    }

    //return the last monitored status
    public synchronized byte getStatus() {
        return prevStatus;
    }

    //recovery action
    public void execute() {
        info.process();
    }
}

 モニタリング可能オブジェクトは、Infoオブジェクトにおけるステータスの変化を絶えず把握し、StatusManagerスレッドに対して通知を行います。StatusMonitorStatusManagerとの通信は、このモニタリング可能オブジェクトを介して行われます。

 モニタリング可能オブジェクトによってsetChangedメソッドの呼び出しが行われると、StatusManager(リスト3参照)が通知を受け取ります。通知を受けたStatusManagerクラスは、該当するモニタリング可能オブジェクトをキューから取り出します。取り出したモニタリング可能オブジェクトの状態が「異常」であれば、StatusManagerクラスはそのオブジェクトのexecute()メソッドを実行します。これにより、モニタリング可能オブジェクトに関連付けられたInfoオブジェクトのprocess()メソッドが呼び出されて、修復処理が実行に移されます。

リスト3 StatusManagerクラス
public class StatusManager extends Thread {
    //add to the queue and
    //notify that the status of monitorable has changed
    public synchronized void setChanged
        (Monitorable monitorable)
        monQueue.add(monitorable);
        notifyAll();
    }
    
    //get the Monitorable object from the queue
    public synchronized Monitorable getMonitorable() {
        // return the object from the queue    
        while(monQueue.isEmpty()) {
            try {
                wait();
            } catch(Exception ex) {}
        }
        return (Monitorable) monQueue.remove(0);
    }

    //report or execute corrective action
    private void processChange() {
        Monitorable monitorable = getMonitorable();

        //take corrective action
        if(monitorable.getStatus() == NOT_OK) {
            monitorable.execute();
        }

        //log or report the status
    }

    public void run() {
        while(true) processChange();
    }
}  

 今回の例では、StatusManagerスレッドのコンテキスト内で復旧タスクの実行を行っていますが、StatusManagerを拡張してスレッドプールを持たせれば、別のスレッドで復旧タスクを実行させることもできます。

 このフレームワークの起動クラスであるLWMFrameworkは、フレームワークの設定と初期化のために利用されます。図2のシーケンス図は、さまざまなフレームワークオブジェクト間のインタラクションを示しています。クライアントコードは、すべてのInfoオブジェクトの追加を終えてからこのフレームワークを起動させ、その途中でStatusManagerおよびStatusMonitorの各スレッドが開始されるのがわかります。

図2 フレームワークのシーケンス図
図2 フレームワークのシーケンス図

同期化

 StatusManagerクラスには、モニタリング可能オブジェクト用のスレッドセーフなキューが用意されています。スレッドは、このキューからモニタリング可能オブジェクトを取り出し、そのgetStatus()メソッドを呼び出します。モニタリング可能オブジェクトのステータス変数へのアクセスもまた同期化されています。

フレームワークの利用

 ここでは、Apache Webサーバのプロセスを監視し、ダウン時にはプロセスを再起動するという例を考えてみましょう。そのために、Infoインターフェイスを実装するProcessInfoクラスを作成します。

 リストされたプロセスの1つ1つに対して「apache.exe」プロセスが実行中であるかどうかをチェックするためのisOk()メソッドをコーディングし、さらに「apache.exe」を起動するためのprocess()メソッドをコーディングします。

public class ProcessInfo implements Info
{
    ProcessInfo(String processName, String execName){ ..}
   //check if process is running or not
    boolean isOk() throws Exception {..} 
   //re-launch process
    boolean process() {..}; 
}

 現状のJavaでは、プロセス一覧、ディスク使用量、CPU負荷、ネットワーク接続状況などを取得するための手段はAPIレベルではサポートされていません。私のプロジェクトでは、こうした情報取得のための関数をOS別にC言語で書き、JavaのコードからアクセスできるようにJNIラッパを用意しました。かなり効率的ではありますが、JNIによる呼び出しがJVMプロセスのコンテキスト内で実行されるため、この解決法は少々危険だといえます。

 代わりに、ネイティブ関数にアクセスするための別のプロセスを用意することもできるでしょう。本稿のダウンロードサンプルには、「CheckProcess.exe」というWin32アプリケーションが含まれています。このアプリケーションは、プロセス名をコマンドラインパラメータとして取得し、そのプロセスが実行中か否かに応じてTRUEまたはFALSEを標準出力に書き出します。ProcessInfoクラスのisOk()メソッドは、この「CheckProcess.exe」を実行し、そのプロセスの入力ストリームから実行中か否かのステータスを読み取ります。

public boolean isOk() throws Exception {
    String[] cmdArray = new String[] 
        {“CheckProcess.exe”, “apache.exe”};
    Process proc = Runtime.getRuntime().exec(cmdArray);
    BufferedReader br = new BufferedReader(new 
        InputStreamReader(proc.getInputStream()));
    String status = br.readLine();
    if(status.equals("TRUE")) return true;
    return false;
}

 続いて、ProcessInfoオブジェクトを設定してフレームワークを開始させるためのTestClientを次のように実装します。

public class TestClient {
    public static void main(String[] args) 
        throws Exception {
        ProcessInfo p = new ProcessInfo(“apache.exe”,
                 “<Path>/apache.exe”);
        int counter = 10; //10 seconds
        byte criticality = LWMFramework.HIGH; 

        LWMFramework lwmf = new LWMFramework();
        //set minimum monitorable frequency 1 second
        lwmf.setMonitorInterval(1);
        //add info object
        lwmf.add(p, counter, level);
        //start the framework
        lwmf.start();

        Thread.currentThread().join();
    }
}

フレームワークの拡張

 このフレームワークはいろいろな方法で拡張することができます。1つの方法として、リモートプロセス、CPU負荷、帯域利用率といったさまざまなコンポーネントを監視するための新しいInfoオブジェクトを実装するやり方があります。こうした新しいInfoオブジェクトは、XML形式の設定ファイル「Monitor.xml」(リスト4参照)の中で設定できます。

リスト4 XMLによる設定(「Monitor.xml」)
<MonitorServer interval="1" > 
<!-- The interval determines unit value of counter in seconds,
 if this value is 60 then minimum frequency of monitoring is
 60 sec or 1 min !-->
<MONITORABLE name = "Apache Webserver" class = "ProcessInfo"
    counter = "1000" criticality = "HIGH">
<!-- ProcessInfo specific configuration !-->
<process name="apache.exe" exec="C:/Apache.exe"/>
</MONITORABLE>
<MONITORABLE name = "Tomcat" class = "ProcessInfo"
    counter = "1000" criticality = "HIGH">
<process name="tomcat.exe" exec="path../tomcat.exe"/>
</MONITORABLE>
<MONITORABLE name = "C Drive" class = "FileSystemInfo"
    counter = "100000" criticality = "HIGH">
<filesystem path="C:" threshold="80" exec="C:/../diskcleanup.exe"/>
</MONITORABLE>
<MONITORABLE name = "app server" class = "ConnectivityInfo"
    counter = "1000" criticality = "HIGH">
<connectivity host="198.168.1.10"/>
</MONITORABLE>
<MONITORABLE name = "LocalMachine" class = "CPUInfo"
    counter = "10000" criticality = "LOW">
<Threshold value="80"/>
</MONITORABLE>
</MonitorServer>

 ここでは、モニタリング可能オブジェクトのそれぞれが、1つのXMLノードとして表され、名前、カウンタ、クリティカル性およびクラスといった共通の属性を持っています。このクラス属性は、Infoオブジェクトのインスタンスを作るときに利用されます。モニタリング可能オブジェクトを表すノード下の要素や子ノードは、対応するInfoオブジェクトの設定を行うために利用されます。また、新たなInfoオブジェクトを実装して「Monitor.xml」ファイルに追加することができます。そうすれば、フレームワークのソースコードを修正することなく新しいコンポーネントを追加することが可能になります。

 このフレームワークをさらに拡張し、レポートや統計情報を生成するサードパーティアプリケーションとのインターフェイスを実装することもできます。また、StatusManagerクラスにHTTPインターフェイスを用意して、コンポーネントをWebブラウザから管理できるようにすることも可能です。

JMXの軽量な代替手段

 ここで紹介したモニタリングフレームワークは簡単に拡張でき、とりわけサーバ側アプリケーションのモニタリングで大いに役立ちます。この軽量なフレームワークは、既存の管理ツールを完全に置き換えるものではありません。むしろ、そうした管理ツールを補完するものであり、HP OpenViewのようなハイエンドの管理ソフトウェアに簡単に統合することができます。

著者紹介

Narendra Venkataraman(Narendra Venkataraman)
Hewlett-Packard社電子通信部門の上級デベロッパ。
関連テーマ
このエントリーを含むはてなブックマーク この記事をクリップ!
BuzzurlにブックマークBuzzurlにブックマーク Yahoo!ブックマークに登録
この記事をokyuuへインポート
最新トップニュース
データメーション
【データメーション】
中国が「Green Dam」フィルタ規制を撤回(7月1日)
Graphic Design Forum
【Graphic Design Forum】
Chris Dickman(6月25日)
プライバシー ジャパン・インターネットコム版
【プライバシー ジャパン・インターネットコム版】
グーグル・ストリートビューの問題について総務省の見解(6月23日)
エンジニアの独り言
【エンジニアの独り言】
システムを「使う」時代のエンジニアに求められるもの(6月2日)
最新ハイテク講座
最新ハイテク講座
電気は家庭でつくる時代へ!燃料電池「エネファーム」(7月3日)
アクセス解析で見るWebマーケティング
アクセス解析で見るWebマーケティング
決定力を探るアクセス解析(7月3日)
百式のネットビジネス研究
百式のネットビジネス研究
ファーストフードを高級っぽく盛り付けて紹介している「Fancy Fast Food」(7月3日)
週刊-サイト別アクセス状況データ
週刊-サイト別アクセス状況データ
ビデオリサーチインタラクティブ調査(月間インターネットオーディエンスデータ)(7月2日)
成約率、反応率を上げる Web 文章術
成約率、反応率を上げる Web 文章術
言葉がダイレクトにキャッシュを生む(7月2日)
不況時代の Web ビジネス最適化講座
不況時代の Web ビジネス最適化講座
アクセス解析エキスパートここだけの話、Web コンシェルジュの“勉強法”こっそり教えます(7月2日)
「Webからの脅威」―その傾向と最新対策
「Webからの脅威」―その傾向と最新対策
不正プログラムの分類(7月1日)
DevX
DevX
JavaScriptとDOMによる動的なWebページの作成(6月30日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
今のままで大丈夫?3匹の子ブタ的キャリア危険度診断(6月30日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
Web サイトは「無駄な穴のたくさん開いたじょうご」〜サイト成果向上の基本的な考え方(6月30日)
Copyright 2009 Japan Internet.com K.K. All Rights Reserved.http://www.internet.com/