![]() ![]() ![]() ![]() エンタープライズソフトウェア構成をもっとシンプルにこの記事のURLhttp://japan.internet.com/developer/20080122/26.html
著者:Michael S. Jones
海外internet.com発の記事
はじめに構成情報の維持および配布を目的とする構成システムは、あらゆるエンタープライズソフトウェアシステムの基幹的コンポーネントです。サーバーが数台しかない小規模の企業では、レジストリ項目と個々の構成ファイルから成る非常に単純な構成管理ソリューションで済みます。しかし、組織の規模が拡大して複雑さが増し、物理的に複数の場所で多くのコンピュータを稼動させるようになると、関連するソフトウェアコンポーネントを正しく構成することは重大な課題になります。本稿では、構成情報の保管に利用できるさまざまなストレージ戦略を説明し、簡単に実装できる構成システムを提案します。 まず、大企業のサーバーとソフトウェアコンポーネントの構成プロパティを管理する便利なシステムに必要な属性を考えてみましょう。このようなシステムの主な特性を優先順位の高い順に示すと、次のようになります。
実際に設定値が中央に保管されているか、ツールを使って中央集中ストレージのように見せるかに関係なく、設定値は中央で一括管理しなければなりません。
システムはプロパティ値をモジュール、サーバー、またはソフトウェアが動作する場所(たとえば、ソフトウェアの特定の部分、つまりモジュール)に割り当てることができるだけでなく、もっと汎用性の高いホストサーバーやロケーション(1次データセンタや災害復旧施設などの物理的な場所)にも割り当てることができなければなりません。この機能によって(たとえば)サーバーレベルでログファイルディレクトリを設定する(同じサーバーにあるすべてのソフトウェアモジュールを同じディレクトリに記録する)ことや、ロケーションレベルでエラーレポートWebサービスを割り当てる(1つロケーションにあるすべてのサーバー上で実行されているすべてのソフトウェアモジュールが同じ値を共有する)ことをサポートします。
万一、ネットワーク障害やデータベース障害が発生しても、ソフトウェアコンポーネントは「適切に」動作するために必要な構成情報を取得できなければなりません。このような障害の際、ソフトウェアは完全には実行できないかもしれませんが、入手可能な構成により、少なくともエラーをログに記録したり、メッセージを動作可能なコンポーネントに転送したり、復旧するまで作業要求をキューに入れたりできることが必要です。
数多くのサーバーやソフトウェアコンポーネントが非常に速く構成情報を読み取っている場合でも、構成システムは需要の増加に対応できなければなりません。
ストレージと配布戦略これまで、ソフトウェア構成情報を保管する主要な方法は、(1)レジストリ、(2).configファイルと.iniファイル、(3)中央集中データベース、および(4)カスタムテキストまたはXMLファイルの4つでした。これらの戦略はそれぞれに長所がありますが(変更の容易さ、ツールの可用性、取得の速さなど)、エンタープライズソフトウェア環境の規模が拡大して複雑さが増すにつれて、それぞれ問題が出てきました(表1を参照)。 表1 構成情報の代表的なストレージ戦略とその長所・短所
構成システムを構築するこれから説明する構成システムには2つの目標があります。1つは中央構成ストレージ、もう1つはエンタープライズレベルでの構成プロパティの配布です。 著者注
本稿に出てくる「モジュール」という用語は、単一のエンティティとして実行(または構成)されている、ソフトウェアの論理的な部分のことです。「ロケーション」とは、類似の構成を持つサーバーの物理的または論理的なグループのことです。また、「プロパティ値」とは、ソフトウェアシステムの動作に影響を与える名前付きの情報のことです。
構成データを保管する分散されたソフトウェアコンポーネントに構成データを配布するプロセスを考える前に、まずデータを中央にどう保管するかを理解することが必要です。構成システムを利用すれば、プロパティ値をソフトウェアモジュール、サーバー、およびロケーションのすべてに割り当てる(またはどれにも割り当てない)ことができます。 図1に、本稿で提案する構成システムのデータモデルを示します。escPropertiesテーブルは、各プロパティに名前(PropertyLabelフィールド)を付与し、これらのプロパティ値のローカルキャッシュを更新する頻度(ValidForフィールド)を定義します。escModulesテーブルは、エンタープライズシステムを構成する各種のソフトウェアモジュールを定義します。escServersテーブルとescLocationsテーブルは、エンタープライズソフトウェアシステムの一部として動作するサーバーとロケーションを定義します。locationsテーブルとserversテーブル間の外部キーリレーションシップにより、構成システムはサーバー名が付与されたロケーションを自動的に検出することに注意してください。 図1の中心にあるescPropertyValuesテーブルは、プロパティに値を割り当てる方法を提供します。このテーブルの指定により、関連するモジュール、サーバー、およびロケーションに応じてプロパティ値を変化させることができます。具体的なモジュール、サーバー、またはロケーションに関連付けないプロパティ値割り当てをサポートするには、値にゼロを指定します。ゼロを指定すると、テーブルにワイルドカードマッチング機能が提供されます。例として、表2に示すescPropertyValuesレコードセットを見てみましょう。 表2 escPropertyValuesの例
表2の1行目のレコードは、ロケーション2239にあるサーバー729上で実行されているモジュール1113のID 1017のプロパティに、値SVRNAMElogfilesabcを提供します。 同様に、2行目は、ロケーション2239にある任意のモジュール(0)または任意のサーバー(0)のID 1017のプロパティに、値SVRNAMElogfilesを提供します。 3行目は、ロケーション2239にあるサーバー729上で実行されている任意のモジュールのID 1017のプロパティに、値OTHERSVRNAMElogfilesを返します。 4行目は、ロケーション2239にある任意のサーバー上で実行されているID 1113のモジュールのID 1017のプロパティに、値SVRNAMElogfilesSを提供します。 最後の5行目は、完全なワイルドカードレコードです。これにより、社内の任意のモジュールがID 1017のプロパティの値を要求した場合は、値SVRNAMElogfilesapp01が返されます。 クライアントソフトウェアがプロパティ値レコードを照会するときには、モジュールID、サーバー名、および取得しようとする値の名前を指定しなければなりません。サーバーのロケーションは、escServersテーブルとescLocationsテーブルのリレーションシップにより導かれます。これらの値を使って、次に示すSQLクエリは入力データに基づき最も具体的なプロパティ値を取得します。 SELECT TOP 1 PropertyLabel, PropertyValue, ValidFor FROM eacPropertyValues pv JOIN eacProperties p ON pv.PropertyID = p.PropertyID LEFT JOIN eacServers s ON pv.ServerID = s.ServerID LEFT JOIN eacLocations l ON s.LocationID = l.LocationID WHERE p.PropertyLabel like 'Log File Location' AND (pv.ModuleID = 1017 OR pv.ModuleID = 0) AND (s.ServerName like 'CORP_SVR' OR pv.ServerID = 0) AND (pv.LocationID = s.LocationID OR pv.LocationID = 0) ORDER BY pv.ModuleID DESC, pv.ServerID DESC, pv.LocationID DESC クライアントの要求に関係するレコードのみを選択するために、クエリでは、指定された検索条件に正確に一致するか、フィールド値が0(ワイルドカード設定値)のレコードだけを抽出します。前述のクエリでは、モジュール、サーバー、またはロケーションを参照するWHERE句内の3つのフィルタ式がこの機能を提供しています。それぞれの式が、クライアントが指定した具体的な値またはワイルドカード値のいずれかと一致することに注意してください。 クライアントが指定した条件に最も適合するレコードを選択するため、このクエリでは、条件との突き合わせの優先順位をモジュール、サーバー、ロケーションの順にしています。具体的には、ModuleID、ServerID、およびLocationIDによる結果セットを降順に並べることで、これを実現しています。結果の並べ替えでは、ゼロ以外の設定値が優先されます。つまり、最上位のレコードのみを選択することで、クエリの結果は、プロパティ値を取得しているソフトウェアに最も具体的に適合する単一のプロパティ値の結果になります。 図1に示した各テーブルと、このような形式のクエリを組み合わせることで、中央から最も適切な構成データを取得するための基本システムを構築できます。これは、柔軟性に優れた構成システムを実現するという目標を満たすことにもなります。この手法を利用すれば、必要に応じて具体的または汎用的にプロパティ値を割り当てることができます。このようなプロパティ割り当てを行うためのシンプルな構成ツールは簡単に作成できるでしょう。 構成システムのもう1つの目標は、中央での一括管理を実現することです。本稿で提案する構成システムは、中央のSQL Serverデータベースに基づいています。ただし、前節で説明したように、このような構成はクライアントシステム数が増加したときにスケーラビリティ問題を引き起こす可能性があります。もっと問題なのは、データベース接続やネットワーク接続に障害が生じると、クライアントソフトウェアは構成情報を取得できなくなり、その結果、全社的なソフトウェア障害が引き起こされることです。 この問題を解決するため、次の節では、中央集中データベースから構成値を取得して、その値をクライアントサーバー全体に配布するための戦略を説明します。 構成値を配布する本稿で提案する構成システムには、中央集中データベースから取得した値をローカルのクライアントサーバーに保管しておき、後からローカルで取得できるようにするための非常に簡単なキャッシュ機構が含まれています。プロセスをシンプルで柔軟にするため、図2に示す簡単なクラスフレームワークを利用することで、ローカルクライアントが中央集中データベースから構成値を読み取り、ローカルキャッシュ(Windowsレジストリ)に保管して、常に最新の値を維持できるようにします。 この構成のキャッシュ戦略には重要な長所がもう1つあります。アプリケーションの始動時に中央のストレージシステムを利用できない場合、従来であれば多くのアプリケーション構成値を取得しようとしても、アプリケーションは始動しないか、不安定な状態のままかのいずれかです。そこで、持続性のあるローカルキャッシュを使うことで可用性を向上させます。 このフレームワークの中心となるのは、クライアントアプリケーションがプロパティ値を読み取る場合に使うPropertyValueクラスです。このクラスにより、クライアントは、構成情報がリモートで管理されてローカルにキャッシュされていることを意識せずに済みます。PropertyValueクラスは、まず要求されたプロパティ値をローカルキャッシュでチェックします。値がローカルで見つかった場合は、クライアントに直ちに値を返します。見つからなかった場合は、中央のデータベースから値を取得し、ローカルに保管してから、クライアントに値を返します。 図3に、PropertyValueクラスが使用するフローの概要を示します。 図3 プロパティ値のフローチャート: クライアントがプロパティ値を読み取ったときに生じるフロー ![]() PropertyValueクラスには、キャッシュ内の値が有効期限切れかどうかを判断するLifetimeプロパティがあります。この場合の有効期限とは、単に値を中央のデータベースから再取得する必要があることを意味します。この時効プロセスにより、社内のすべてのマシンでプロパティ値のローカルコピーに対し定期的な更新が適用されます。escPropertyValuesテーブルのValidForフィールドは、ローカルのプロパティ値を次にリフレッシュするまでの秒数を表します。クライアントが有効期限切れの値を要求すると、クラスは中央のデータベースから新しい値を取得し、ローカルの値を更新してから、クライアントに値を返します。 中央のデータベースにアクセスできない状況に対処するため、このクラスは次のように振る舞います。
このスキームにより、データをできる限り最新の状態に維持しつつ、中央のデータベースサーバーにアクセスできない不測の事態が生じた場合もプロパティ値をできる限りローカルで取得できるようにします。 拡張性読者の中には、このソリューションで中央のリポジトリにSQL Serverを使用していることに異議を唱える方もいるでしょう。プロパティのローカルリポジトリとしてWindowsレジストリを使っていることに異議を唱える方はもっと多いかもしれません。個人的には、これらはストレージとしておそらく最適な万能の選択肢であると考えますが、図2に示すクラスフレームワークは、ローカルストレージにおいてもリモートストレージにおいても交換可能な互換性をサポートしています。ほとんどの場合、プロパティ値の中央リポジトリとして使用するならばSQL Serverなどのエンタープライズクラスのリレーショナルデータベースが最適な選択肢だと思われますが、サーバーでWebアプリケーションを実行することが多い場合は、ローカルストレージにASP.NETキャッシュを採用する方がよいかもしれません。 LocalStorageClientとRemoteStorageClientに組み込まれている拡張性のメカニズムの働きはほとんど変わりません。このフレームワークは、他のローカルやリモートのストレージメカニズムをサポートするように簡単に拡張することができます。たとえば、ローカルの構成プロパティをXMLファイルに保管するとしましょう。手順は次のとおりです。 まず、LocalStorageClientを継承する新しいクラスを作成し、 RemoteStorageClientで使われるメカニズムへの参照はないことに注意してください。また、ローカルに保管されている値が有効期限切れかどうかをチェックする場合に使うコードもありません。これは、PropertyValueクラスがこれらの処理を受け持つためです。プロパティ値をリモートストレージから更新しなければならない場合、PropertyValueクラスは、図3に示すように、RemoteStorageClientとLocalStorageClient間の対話を調整します。 サンプルプロジェクトを使用する本稿のダウンロードサンプルには、中央のSQL Serverデータベースからプロパティ値を取得して、ローカルのコンピュータのレジストリに保管する、完全に機能するプロジェクトが収められています。ローカルに保管されたプロパティ値は、escPropertyValueテーブル内のValidForの設定値に従い、SQL Serverシステムから取得した値でリフレッシュされます。 PropertyValueクラスのGetValue共有メソッド関数の呼び出しがシステムへのエントリポイントです。GetValueには、ModuleIDと、取得対象のプロパティの名前のみを渡す必要があります。RemoteStorageClientクラスがサーバー名を取得するので、テーブルはそのサーバー名を使用してロケーションを推測することができます。システムは、要求されたプロパティの値をローカルストレージまたはリモートストレージのいずれかから取得できる場合、そのプロパティのPropertyValueオブジェクトを返します。 テーブルにデータを設定する最初に、データベーステーブルにデータを設定するため、実装予定のプロパティごとにescProperties行を作成します。プロパティIDが0のプロパティ行は作成しないでください。プロパティ名ではワイルドカードはサポートされていません。プロパティ値は会社によって大きく異なりますが、ほとんどの機関が、LogFileLocation、DebugLevel、ErrorReportingWebServiceURLなどのプロパティラベルを採用しているようです。 次に、別々に構成するモジュールごとにescModulesテーブルに行を作成します。ModuleIDがゼロのワイルドカード行も作成する必要があります。個人的には、実行可能ファイルに対するモジュール行だけでなく、マシンで実行するすべてのアセンブリに対してモジュール行を作成することをお勧めします。こうすれば、システム上のソフトウェアの各部分ごとに固有の構成情報を追加することができます。 最後に、社内のサーバーごとおよびロケーションごとに、escServersテーブルとescLocationsテーブルに行を追加します。また、前述の理由から、これらの両方のテーブルにもワイルドカード行を追加します。 本稿のダウンロードサンプルには、escTestというサンプルクライアントアプリケーションが含まれています。このクライアントは、フレームワークのしくみを理解したり、新しいローカルおよびリモートのストレージフレームワークをテストしたりするときに役立ちます。また、このクライアントを使って、SQL Serverデータベースが正しく構成されているかを確認することもできます。 複数の値を取得し、その後も取得した値がローカルキャッシュから返されていることを確認したら、レジストリを開き、ローカルのプロパティ値を検査します。レジストリエディタを使って、HKEY_Local_MachineSoftwareEnterpriseSoftwareConfigurationに移動します。プロパティ値の取得に使ったModuleIDごとに、サブキーを探します。各ModuleIDのサブキー下に、値を取得したプロパティラベルごとに別々のサブキーがあります。PropertyValueレジストリキーとValidUntilレジストリキーは、プロパティラベルのサブキー下に保管されています。図4に、レジストリに階層構造で保管されているModuleID 1003の"Log File Location"プロパティを示します。 図4 ローカルのレジストリストア: LocalStorageClient_Registryクラスは、ModuleIDプロパティとPropertyLabelプロパティを使ってプロパティ値を階層構造で保管する モジュールIDに基づいてすべてのプロパティ値を保管するということは、すべてのモジュールで共有されるプロパティ値(escPropertyValuesテーブルではModuleIDが0のプロパティ値)は重複して保管されるということです。言い換えれば、同じプロパティ値を参照するモジュールでも、モジュールごとに独自のコピーが保管されるということです。長所がいくつもあることを考えれば、これは許される範囲の犠牲です。ストレージを実装する方法が非常にシンプルであるだけでなく、このストレージスキームを採用すると、プロパティ値のストレージをデバッグするプロセスも簡素化されます。ModuleID 0に値を割り当てても、プロパティ値のすべてのインスタンスが同じになるわけではないということを思い出してください。このモデルでは、ModuleID 0に対する汎用プロパティ値を設定できる一方で、特定のモジュールについては同じプロパティに具体的な値を割り当てることができます。 また、ローカルキャッシュストレージにレジストリを使うという方法は、Windowsプラットフォームでしか使用できないことにも注意しましょう。他のプラットフォームには別のローカルストレージソリューションが必要です。 社内にプロジェクトを実装するこの構成システムがどの組織でも柔軟に機能することを示すために、サンプルプロジェクトの機能とソースコードはあえてシンプルにしておきました。プロジェクトを実装する前に、このプロジェクトがどのような目的に適したものかを確認しておきましょう。このフレームワークは、使用頻度の高いアプリケーションデータのキャッシュを目的としたものではありません。つまり、汎用的なキャッシュとは考えないでください。 このフレームワークの構築に使用した技法の多くは、このようなキャッシュ設計から取り入れていますが、大容量での使用のために、つまりもっと多くのデータを保管するためには、ローカルストレージクライアントとリモートストレージクライアントの設計を別に最適化する必要があります。すでに説明したように、このシステムは、レジストリや.NETのapp.configファイルを介してソフトウェア構成を管理する単に1つの選択肢にすぎません。 また、実際にプロジェクトを配備する前に、次のリストに挙げた修正の少なくとも一部を適用することを検討してください。
企業が成長すると、複数のサーバー上でソフトウェア構成プロパティを同期させておくことは困難になります。このサンプルコードで実現したプロジェクトをベースにして、各自のニーズに合わせて拡張し、独自のエンタープライズ対応ソフトウェア構成フレームワークを構築してみてはいかがでしょうか。 著者紹介Michael S. Jones(Michael S. Jones)
米国の医療テクノロジプロバイダ、Systems Architecture for Passport Health Communications, Inc.,の役員。同社は病院、医療クリニック、および外来診察センター間に支払人情報および患者情報を取り扱うネットワークを構築し、医療機関の収益循環プロセスを促進および改善するためのソリューションを提供している。妻と3人の子供と共にテネシー州フランクリンに住み、余暇には読書や家族と共にアウトドアを楽しむ。
|