2

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

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

我看过 Apache Commons Configuration,它内置了对多租户配置的支持,但据我所知,这是通过将一些基本配置一些覆盖组合来完成的。这意味着我将有一个配置指定:

application.lang=english

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

application.lang=klingon

不幸的是,如果我们的支持团队可以在一个地方查看所有相关配置,并且以某种方式内联指定覆盖,而不是为base和overrides分别设置文件,那么他们会容易得多。

我认为 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是一个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*网站上找到该文档。

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

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

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

使用 Config4* 在服务器应用程序(由 bacar 的评论提示)中使用 Config4* 的另一种方法如下...

像下面的伪代码一样实现每个服务器操作:

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变量一起放在全局范围中。我这样做的原因超出了您的问题范围:将“注入”变量与应用程序可见变量分开可以更轻松地对应用程序可见变量执行模式验证。

于 2012-05-16T20:10:35.453 回答
0

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

于 2012-05-16T17:39:34.653 回答