【问题标题】:Java Web Application Configuration PatternsJava Web 应用程序配置模式
【发布时间】:2010-12-06 20:10:31
【问题描述】:

是否有任何模式或最佳实践可用于简化跨多个环境的 Java Web 应用程序更改配置文件。例如JDBC URL、SOAP 端点等。

作为帮助澄清我的问题的一些背景知识,我使用了几个大型 Java Web 应用程序,这些应用程序在任何给定的发布周期中都会经过 6 个不同的环境;开发、集成、质量保证、性能并最终部署到多个生产服务器。在每个环境中,配置都需要更改。目前,每个部署的大多数配置更改都是手动完成的,这既费时又容易出错。
有什么办法可以让人工干预脱离这个过程?

【问题讨论】:

  • JNDI (java.sun.com/products/jndi) 是专门为解决这个问题而创建的,我通常用于生产系统,因为它可以很好地扩展(无论我有 1 个环境还是6)。它还明确区分了开发人员和系统管理员的职责。话虽如此,如果您知道您将只有 6 个环境,并且这个数字几乎不会改变,那么 Jeremy 的解决方案应该可以工作,我相信这是配置框架(例如 Grails)使用的新约定。
  • 顺便说一下,JNDI 设置需要手动设置,但每个环境只需执行一次(有点像安装应用服务器或环境操作系统)。

标签: java web-applications configuration design-patterns


【解决方案1】:

您可以在您选择的语言中使用组件配置模式

在POSA书籍中有描述(我认为在第4卷)

(在java中你可以使用commons-configuration组件)。

【讨论】:

    【解决方案2】:

    请看一下这个网址:http://issues.apache.org/jira/browse/CONFIGURATION-394

    我们正在寻找的配置框架是 Apache Commons Configuration 之上的东西,并且必须支持并发问题、JMX 问题和大多数存储(例如 .properties 文件、.xml 文件或 PreferencesAPI)。

    weblogic 团队在“管理控制台”上提供的内容很有趣,通过它您可以对配置进行事务性(原子)更新,以便通知已注册的侦听器。

    Apache 家伙坚持认为这个项目超出了 Commons Configuration 的范围,也许吧!

    我附上了一个简单的配置框架,请看一下。

    【讨论】:

    【解决方案3】:

    我很惊讶没有人引用 Jakarta Commons Configuration API (http://commons.apache.org/configuration/) 来回答这个问题。它允许您拥有文件的层次结构(或其他配置源,如 XML、JNDI、JDBC 等)。这就是 Jeremy Seghi 所说的内容,它为您提供了一种同时拥有默认值和本地覆盖的好方法。

    最好的部分是它是经过测试的有效解决方案,因此您不必自己动手制作。

    【讨论】:

    • +1 表示此建议。自从最初发布这个问题以来,我才知道这个库。它应该非常有用,尽管到目前为止我还没有看到基于当前环境自动加载不同配置文件的内置方式。
    【解决方案4】:

    Seam 或Grails(借用自Rails)中使用了您想要的很好的例子。有配置文件,默认情况下三个:prod、dev、test,但您可以根据需要定义更多。

    Seam 项目的构建是由 Ant 文件完成的。每个配置文件都定义了内容可能不同的每个文件,例如数据源、sql 脚本或属性文件。

    import-dev.sql
    import-prod.sql
    import-test.sql

    当使用选择的配置文件运行 ant 文件时,会采用适当的文件并从该文件名中截断配置文件名称。

    下面是可以放在目标中的代码 sn-p

    <copy tofile="${war.dir}/WEB-INF/classes/import.sql" 
          file="${basedir}/resources/import-${profile}.sql"/>
    

    JDBC url,驱动名称可以外化为属性文件(当然以profile名称为后缀)

    <filterset id="persistence">
         <filter token="transactionManagerLookupClass" value="${transactionManagerLookupClass}"/>
    
    <copy tofile="${war.dir}/WEB-INF/classes/META-INF/persistence.xml" 
         file="${basedir}/resources/META-INF/persistence-${profile}.xml">
         <filterset refid="persistence"/>
    </copy>
    

    或者你可以从命令行传递给 ant build 调用的属性值。这是 Seam 中的简短示例。

    另一个选项是使用Maven。以 Maven 方式,它由属性和 profiles 完成,但您也可以使用单独的模块来拆分配置并创建具有主要功能的其他模块。 maven 属性和配置文件的典型用例示例是多个数据库、部署服务器等的运行配置。当您想为不同的供应商创建配置时会更加困难,但对于 Maven,这不是问题 :)

    使用 maven 配置文件的一个很好的例子是这个帖子表单Carlos Sanchez blog

    总之,我强烈建议查找 Ant/Seam 一个 Maven 参数化(配置文件)。这些解决方案还有另一个优势:ant 或 maven 脚本可以在 CI 服务器中运行(如 Hudson)并让您同时运行/测试所有配置文件。

    【讨论】:

      【解决方案5】:

      我最近倾向于更多地使用 .NET,所以我的 Java 相当生疏。我敢肯定,只要稍加调整,这将适用于任何语言。

      我们使用 .NET 配置系统的扩展,它允许我们将环境和/或应用程序特定设置与更全局的配置结合使用。配置系统使用全局设置将每台机器标识为开发、测试或生产(默认)。一组按顺序加载的文件,最后一个文件的设置会覆盖之前加载的文件中定义的任何设置。文件按以下顺序加载:

      1. 全局设置
      2. 应用程序特定设置
      3. 应用程序特定的环境覆盖

      所有文件都在源代码管理中,并且由于环境是在应用程序运行的机器上定义的;因为除非机器配置将其标识为“beta”,否则它不会访问“beta”配置,因此我们可以提升所有配置文件,而不必担心无意中将我们的生产应用程序指向开发数据库。

      【讨论】:

      • 我认为这是在应用部署过程中无需任何人工操作,根据当前环境自动检测和加载配置问题的最佳答案。它还允许同一环境中的服务器之间的配置可能不同的情况。按照 John Munsch 的建议,使用它并使用 Commons Configuration 进行覆盖将有助于解决问题。
      【解决方案6】:

      我们所做的工作非常好。

      在启动时,我们的程序会读取硬编码路径中的配置文件。假设它是:

      /config/fun_prog/config.xml
      

      每个程序都有不同的硬编码路径(FunProgram 在 fun_prog 中,Super Server 在 sup_serv 中,等等),所以我们不必担心它们会相互交叉。

      XML 文件由我们创建的一个小配置库读取。 XML 文件包含 DB 连接信息,通常是邮件服务器配置数据、发送通知的电子邮件地址、是否应该在测试模式下运行、外部服务的 URL 等。

      因此,当我们需要进行更改时,我们复制配置文件,编辑我们想要的内容,然后重新启动程序。由于我们有一个标准的服务器设置,任何程序都可以部署在任何服务器上,只需复制这些文件(以及必要的 httpd.conf 修改)。

      它并不花哨,但效果很好。它非常易于理解、添加新的配置选项、备份和编辑。适用于所有平台(unix 很明显,Windows 将以 / 开头的路径转换为 ​​c:\ 所以它也可以在没有编辑的情况下工作)。

      我们的工作站基本上运行与服务器相同的软件,只是对该配置文件进行了一些更改。

      【讨论】:

        【解决方案7】:

        有几种可能的方法来解决这个问题:

        • 像您一样使用属性文件,但添加一个“元属性”文件,该文件用于通过定义环境值(例如 localhost 主机名)到属性文件名之间的映射来选择使用的属性文件加载。

        • 将您的属性放入数据库,并将与应用服务器中属性表的数据库连接定义为您的 Web 应用获取的资源。

        • 不要将属性文件放在 .war 或 .ear 中,而是创建一个包含每个目标主机的属性文件的 properties-deployhost.jar 档案。通过将适当的 .jar 文件添加到类路径(例如,通过每个 Web 应用的应用程序服务器配置中的共享库)将适当的 .jar 文件绑定到已部署的 Web 应用。

        只有第一个在部署时不需要额外的手动步骤,代价是在重命名目标系统时必须更新配置源并构建新的部署文件。

        我确信这些和你的方法有很多变体,什么是最好的选择取决于你的情况。

        【讨论】:

          【解决方案8】:

          以下是我使用或遇到的一些可能的做法。在实践中通常需要将这些结合起来。

          构建时替换conffiles中的变量值

          这是一个如何使用 Apache Ant 完成此操作的示例。 Ant 属性 (${var.name}) 可以通过构建配置文件进行控制:

          <filterset id="variables.to.replace">
              <filter token="APPNAME" value="${app.name}"/>
              <filter token="WEBAPP-PATH" value="${webapp.path}"/>
              <filter token="ENCRYPT-ALGORITHM" value="${encrypt.algorithm}"/>
              <filter token="ERROR-MAILTO" value="${error.mailTo}"/>
              <!--...-->
          </filterset>
          
          <!-- Then, when building & copying the conf, replace the variables: -->
          <copy todir="${properties.target.dir}">
              <!-- env specific conf files -->
              <fileset dir="${basedir}/env/${run.env}/webapp/WEB-INF/classes" />
              <filterset refid="variables.to.replace"/>
          </copy>
          

          好处是您可以在构建时很好地控制不同的配置。不好的是,如果您将这种方法广泛用于大量不同的配置,系统往往会变得非常复杂且难以维护。此外,必须构建配置文件也意味着开发周期变慢。

          在 webapp 启动时替换 conf inside war 中的变量

          这是我在使用 Spring Framework 时通常会做的事情,即使只有一种可能的配置,也可以获得关注点分离的好处。使用 Spring,您可以在 webapp 启动时在 Spring 上下文中将 conf 值替换为 PlaceholderPropertyConfigurer。在这种情况下,无论如何您都必须选择正确的配置,例如可以在构建时进行配置。

          与构建时间替换相比,如果需要,在未压缩的 web 应用中临时操作值更容易。当然,如果您更改任何内容,则需要重新启动 webapp,并且手动更改不会在 webapp 重新部署中持续存在。 Spring 也仅限于 Spring 上下文,因此 this doesnt' work e.g. in web.xml(但由于其局限性,应该避免在 web.xml 中包含变量)。

          从预定义文件中读取本地配置

          这种方法可能是最容易设置的方法:只需创建一个配置文件路径,例如$HOME/mywebapp/conf.properties 并让您的网络应用在启动时以某种方式读取它。

          这里的好处是您在构建/部署 webapp 时不必关心 conf。不管怎样,你应该有一些合理的 conf 默认值,然后可以被本地 conf 覆盖。

          将 conf 保存在数据库中

          这是覆盖 conf 参数的最灵活的解决方案,但在某些情况下也会变得复杂。将 conf 放在包含 namevalue 列的表中应该适用于大多数情况。

          当然,您不能在数据库表中配置 JDBC 连接 url,但对于在设置 db 连接后影响 webapp 操作的简单文本/数字配置,这是一个很好的解决方案。为避免性能损失,请确保以某种方式缓存 conf,如果它会被频繁访问。

          额外练习

          正如 kgiannakakis 所指出的,它还有助于为您的应用设置某种配置诊断页面。

          【讨论】:

            【解决方案9】:

            首先将所有经常更改的配置设置集中在一个位置。如果您需要同时设置 JNDI、编辑数据库值和修改属性文件,以完成配置,这真的很难。首选最容易编辑并且更容易验证所有设置是否正确的媒体。我会说属性文件是最好的解决方案。您可以轻松地编辑它们,您只需要快速浏览它们就可以看到一切正常。如果您选择属性文件,请仔细为其选择标准位置并为路径分配环境变量。

            如果您有一个简单的测试来验证所有设置是否正确,这也会有所帮助。例如,您可以有一个显示配置参数并执行一些基本测试的测试页面,例如尝试连接到数据库或远程服务器。

            【讨论】:

            • 我喜欢测试页的想法。类似的东西会让确认/拒绝不正确的配置变得容易得多。
            • 这不是最佳实践,并且不会像 OP 所要求的那样将手动工作从流程中剔除
            【解决方案10】:

            这在很大程度上取决于 Web 应用程序服务器为您提供的选项。我们有多个具有不同 JDBC URL 的 JBoss 环境,JNDI 名称在所有服务器上保持相同,只是本地实例的配置发生了变化,因此每次构建都不会出错。

            我想简短的回答是,最佳做法是将配置外部化,并为每个服务器保留一个具有正确设置的好文件,并让 Web 应用程序读取该配置。外部化和读取的确切性质将取决于具体的配置和应用服务器。

            编辑:这些配置不作为战争的一部分存在(在我们的例子中是耳朵),因为它们不会被覆盖。

            【讨论】:

            • 感谢您的回答。我们确实将配置外部化,并且这些文件的位置在所有服务器(WEB-INF/config)中都是相同的。但是,每次部署后都需要将配置文件放在该位置。您是否有位于扩展战争之外的文件,或者它们如何没有被覆盖或需要在新安装后放回原处?
            • @Donal Boyle,将您的配置放入 WEB-INF/config 并没有真正将其外部化,因为它仍然是您的 web 应用程序的一部分。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-06-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-04-18
            • 2010-12-29
            • 1970-01-01
            相关资源
            最近更新 更多