2

システムのさまざまなユーザーがシステムと対話するように、サーバー側の Java アプリケーションを構成するためのソリューションを見つけようとしています ( Multitenancy )。たとえば、アプリケーションが user1 からのリクエストを処理する場合、アプリケーションがクリンゴン語で応答するようにしますが、他のすべてのユーザーには英語で応答するようにします。(詳細を避けるために、意図的にばかげた例を選びました。重要なことは、アプリが要求ごとに異なる動作をするようにすることです)。

理想的には、一般的な解決策があります (つまり、コードを変更することなく、構成の任意の部分にユーザー固有のオーバーライドを追加できるようにする解決策)。

マルチテナント構成のサポートが組み込まれている Apache Commons Configuration を見てきましたが、私が知る限り、これは基本構成とオーバーライドのセットを組み合わせることによって行われます。これは、次を指定する構成があることを意味します。

application.lang=english

そして、user1.properties上書きファイルを言います:

application.lang=klingon

残念ながら、 baseと overridesで別々のファイルを用意するよりも、何らかの方法でインラインで指定されたオーバーライドを使用して、関連するすべての構成を 1 か所で確認できる方が、サポート チームにとってはるかに簡単です。

Commons Config のマルチテナンシー +基礎となる構成内の条件付き要素を記述するVelocity テンプレートのようなものの組み合わせは、私が目指しているもののようなものだと思います - 私の構成とのやり取りを容易にするための Commons Config と、オーバーライドを非常に表現的に記述するための Velocity 、単一の構成で、例えば:

#if ($user=="user1")
 application.lang=klingon
#else
 application.lang=english
#end

この種の問題に対して人々はどのような解決策を使用していますか?

4

2 に答える 2

3

次のように、各サーバー操作をコーディングしてもよろしいですか?

void op1(String username, ...)
{
    String userScope = getConfigurationScopeForUser(username);
    String language = cfg.lookupString(userScope, "language");
    int    fontSize = cfg.lookupInt(userScope, "font_size");
    ... // business logic expressed in terms of language and fontSize
}

(上記の疑似コードは、ユーザーの名前がパラメーターとして渡されることを前提としていますが、別のメカニズム (スレッドローカルストレージなど) を介して渡すこともできます。)

上記が受け入れられる場合、Config4*は要件を満たすことができます。Config4* をgetConfigurationScopeForUser()使用すると、上記の疑似コードで使用されているメソッドを次のように実装できます (これはcfg、構成ファイルを解析することによって以前に初期化された構成オブジェクトであると想定しています)。

String getConfigurationScopeForUser(String username)
{
    if (cfg.type("user", username) == Configuration.CFG_SCOPE) {
        return Configuration.mergeNames("user", username);
    } else {
        return "user.default";
    }

}

上記で動作するサンプル構成ファイルを次に示します。ほとんどのユーザーは「user.default」スコープから構成を取得しますが、Mary と John はこれらのデフォルト値の一部を独自にオーバーライドしています。

user.default {
    language = "English";
    font_size = "12";
    # ... many other configuration settings
}

user.John {
    @copyFrom "user.default";
    language = "Klingon"; # override a default value
}

user.Mary {
    @copyFrom "user.default";
    font_size = "18"; # override a default value
}

上記がニーズに合っているように思われる場合は、「Getting Started Guide」の第 2 章と第 3 章を読んで、Config4* 構文と API を十分に理解し、適合性を確認/反論できるようにすることをお勧めします。必要に応じて Config4* の そのドキュメントは Config4* Web サイトにあります。

免責事項: 私は Config4* のメンテナーです。

編集: bacar のコメントに応じて、詳細を提供しています。

Config4* を Maven リポジトリーに入れていません。ただし、Config4* はサードパーティのライブラリに依存していないため、バンドルされている Ant ビルド ファイルを使用して Config4* をビルドするのは簡単です。

サーバー アプリケーションで Config4* を使用する別の方法 (bacar によるコメントによって促される) は、次のとおりです。

次の疑似コードのように、各サーバー操作を実装します。

void op1(String username, ...)
{
    Configuration cfg = getConfigurationForUser(username);
    String language = cfg.lookupString("settings", "language");
    int    fontSize = cfg.lookupInt("settings", "font_size");
    ... // business logic expressed in terms of language and fontSize
}

上記getConfigurationForUser()で使用したメソッドは、次の擬似コードに示すように実装できます。

HashMap<String,Configuration>  map = new HashMap<String,Configuration>();

synchronized String getConfigurationForUser(String username)
{
    Configuration cfg = map.get(username);
    if (cfg == null) {
        // Create a config object tailored for the user & add to the map
        cfg = Configuration.create();
        cfg.insertString("", "user", username); // in global scope
        cfg.parse("/path/to/file.cfg");
        map.put(username, cfg);
    }
    return cfg;    
}

上記で動作するサンプル構成ファイルを次に示します。

user ?= ""; // will be set via insertString()
settings {
    @if (user @in ["John", "Sam", "Jane"]) {
        language = "Klingon";
    } @else {
        language = "English";
    }
    @if (user == "Mary") {
        font_size = "12";
    } @else {
        font_size = "10";
    }
    ... # many other configuration settings
}

2 つのアプローチに関する主なコメントは次のとおりです。

  1. 最初のアプローチ (Configuration多数の変数とスコープを含む 1 つのオブジェクト) は、2 番目のアプローチ (Configurationそれぞれ少数の変数を持つ多数のオブジェクト) よりもわずかに少ないメモリを使用する可能性があります。しかし、私の推測では、どちらのアプローチのメモリ使用量も KB または数十 KB 単位で測定され、サーバー アプリケーションの全体的なメモリ フットプリントと比較すると重要ではありません。

  2. 1 つのオブジェクトが 1 回だけ初期化され、読み取り専用スタイルの操作Configurationでアクセスされるため、私は最初のアプローチを好みます。これは、サーバー アプリケーションがマルチスレッドであっても、オブジェクトlookup()へのアクセスの同期について心配する必要がないことを意味します。対照的に、2 番目の方法では、サーバー アプリケーションがマルチスレッドConfigurationの場合、 へのアクセスを同期する必要があります。HashMap

  3. 形式の操作のオーバーヘッドはlookup()、たとえばナノ秒またはマイクロ秒のオーダーですが、構成ファイルの解析のオーバーヘッドは、たとえばミリ秒または数十ミリ秒のオーダーです (ファイルのサイズによって異なります)。最初のアプローチでは、構成ファイルの比較的コストのかかる解析が 1 回だけ実行されます。これは、アプリケーションの初期化で実行されます。対照的に、2 番目のアプローチでは、比較的コストのかかる構成ファイルの解析が "N" 回 ("N" ユーザーごとに 1 回) 実行され、サーバーがクライアントからの要求を処理している間に繰り返しコストが発生します。このパフォーマンス ヒットは、アプリケーションにとって問題になる場合とそうでない場合があります。

  4. 実装の容易さよりも使いやすさが重要だと思います。したがって、2 番目の方法で構成ファイルの保守が容易になると思われる場合は、その方法を使用することをお勧めします。

  5. settings2 番目のアプローチでは、「注入された」変数と一緒にグローバル スコープではなく、ほとんどの変数を名前付きスコープ ( ) に配置する理由を不思議に思うかもしれませんuser。質問の範囲外の理由でそれを行いました.「注入された」変数をアプリケーションに表示される変数から分離すると、アプリケーションに表示される変数でスキーマ検証を実行しやすくなります。

于 2012-05-16T20:10:35.453 に答える
0

通常、ユーザー プロファイルは DB に入り、ユーザーはログインでセッションを開く必要があります。ユーザー名は HTTPSession (=Cookies) に入り、リクエストごとにサーバーはユーザー名を取得し、DB からプロファイルを読み取ることができます。Shure、DB は joe.properties、jim.properties、admin.properties などの構成ファイルにすることができます。

于 2012-05-16T17:39:34.653 に答える