Java的多租户/条件配置有哪些库?

时间:2012-05-16 15:04:41

标签: java configuration velocity apache-commons-config

我正在尝试找到一个配置服务器端Java应用程序的解决方案,以便系统的不同用户与系统交互,就好像它的配置不同(Multitenancy)。例如,当我的应用程序为来自user1的请求提供服务时,我希望我的应用程序能够在Klingon中进行响应,但对于所有其他用户,我希望它以英语回复。 (我选择了一个故意荒谬的例子,以避免具体细节:重要的是我希望应用程序针对不同的请求采取不同的行为)。

理想情况下,有一个通用解决方案(即允许我添加的解决方案) 用户特定的覆盖我的配置的任何部分,而无需更改代码。)

我已经查看过具有built in support for multitenant configuration的Apache Commons Configuration,但据我所知,这是通过将某些基本配置组合在一起来实现的。覆盖。这意味着我有一个配置指定:

application.lang=english

,并说一个user1.properties覆盖文件:

application.lang=klingon

不幸的是,对于我们的支持团队来说,如果他们能够在一个地方看到所有相关配置,并以某种方式内联指定覆盖,而不是为 base 覆盖提供单独的文件

我认为Commons Config的多租户+类似Velocity template之类的东西的组合来描述底层配置中的条件元素是我的目标 - Commons Config易于与我的配置和Velocity进行交互用于在单一配置中非常明确地描述任何覆盖,例如:

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

人们使用什么解决方案来解决这类问题?

2 个答案:

答案 0 :(得分: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是先前通过解析配置文件初始化的Configuration对象):

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

}

这是一个用于处理上述内容的示例配置文件。大多数用户从“u​​ser.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
}

如果以上听起来可能符合您的需求,那么我建议您阅读“入门指南”的第2章和第3章,以便对Config4 *语法和API有足够的了解,以便能够确认/反驳Config4 *是否适合您的需求。您可以在Config4 * website上找到该文档。

免责声明:我是Config4 *的维护者。

编辑:我提供了更多详细信息以回应bacar的评论。

我没有将Config4 *放在Maven存储库中。但是,使用捆绑的Ant构建文件构建Config4 *是微不足道的,因为Config4 * 对第三方库有依赖性。

使用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
}

我对这两种方法的主要评论如下:

  1. 第一种方法(一个包含大量变量和范围的Configuration对象)可能比第二种方法使用的内存略少(许多Configuration个对象,每个对象的数量都很少变量)。但我的猜测是,任何一种方法的内存使用量都将以KB或数十KB来衡量,与服务器应用程序的整体内存占用量相比,这将是微不足道的。

  2. 我更喜欢第一种方法,因为单个Configuration对象只初始化一次,然后通过只读lookup()样式操作进行访问。这意味着您不必担心同步对Configuration对象的访问,即使您的服务器应用程序是多线程的。相反,如果您的服务器应用程序是多线程的,则第二种方法要求您同步对HashMap的访问。

  3. lookup()样式操作的开销大约为纳秒或微秒,而解析配置文件的开销大约为毫秒或数十。毫秒(取决于文件的大小)。第一种方法仅执行一次相对昂贵的配置文件解析,并且在应用程序的初始化中完成。相反,第二种方法执行相对昂贵的配置文件解析“N”次(每次“N”个用户一次),并且在服务器处理来自客户端的请求时发生重复费用。对于您的应用程序而言,性能损失可能会也可能不是问题。

  4. 我认为易用性比易于实施更重要。因此,如果您认为第二种方法可以更容易维护配置文件,那么我建议您使用该方法。

  5. 在第二种方法中,您可能想知道为什么我将大多数变量放在命名范围(settings)中,而不是放在全局范围内以及“注入”user变量。我这样做的原因超出了你的问题范围:将“注入”变量与应用程序可见变量分开,可以更容易地对应用程序可见变量执行模式验证。

答案 1 :(得分:0)

通常,用户配置文件将进入数据库,用户必须使用登录名打开会话。用户名可以进入HTTPSession(= Cookies),每次请求服务器都将获得用户名,并可以从DB读取配置文件。舒尔,DB可以是一些配置文件,如joe.properties,jim.properties,admin.properties等。