Java Web应用程序configuration模式
是否有任何模式或最佳实践可用于简化在多个环境中更改Java Web应用程序的configurationconfiguration文件。 例如JDBC URL,SOAP端点等
作为一个有助于澄清我的问题的背景知识,我使用了几个大型的Java Web应用程序,在任何给定的发布周期中,都可以通过6个不同的环境。 开发,集成,QA,性能,并最终部署到多个生产服务器。 在每个环境中,configuration都需要改变。 现在,每个部署的大部分configuration更改都是手动完成的,既费时,又容易出错。
有没有办法从这个过程中手动干预?
我最近更倾向于使用.NET,所以我的Java很生疏。 我敢肯定,这将工作在任何语言与一点点调整。
我们使用.NETconfiguration系统的扩展,使我们能够将环境和/或应用程序特定设置与更全局的configuration结合使用。 configuration系统使用全局设置,每台机器将其标识为dev,beta或production(默认)。 按顺序加载的一组文件,最后一个文件的设置将覆盖以前加载的文件中定义的所有设置。 文件按以下顺序加载:
- 全局设置
- 应用程序特定设置
- 应用程序特定的环境覆盖
所有的文件都在源代码控制中,并且由于在应用程序运行的机器上定义了环境; 因为除非机器configuration将其标识为“testing版”,否则不会访问“testing版”configuration,所以我们可以提升所有configuration文件,而不必担心无意中将生产应用程序指向开发数据库。
我很惊讶没有人引用Jakarta Commons Configuration API( http://commons.apache.org/configuration/ )来回答这个问题。 它允许您拥有文件的层次结构(或其他configuration来源,如XML,JNDI,JDBC等)。 这就是Jeremy Seghi正在谈论的事情,它给了你一个很好的方法,既有违约也有本地覆盖。
最好的部分是它是一个经过testing的工作解决scheme,所以你不必自己去制作一些东西。
以下是我使用或遇到的一些可能的做法。 在实践中通常需要结合这些。
在构build时replaceconff中的variables值
以下是Apache Ant如何实现的一个例子。 可以使用构buildconfiguration文件来控制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>
好的是,您可以在构build时对不同的configuration进行良好的控制。 不好的是,如果你广泛地使用这种方法进行大量不同的configuration,系统往往会变得非常复杂和难以维护。 而且,构buildconffiles也意味着更慢的开发周期。
在webapp启动时用战争内的confreplacevariables
这就是我通常在使用Spring框架时所做的事情,即使只有一个可能的configuration,可以获得关注分离的好处。 使用Spring,您可以在webapp启动时使用Spring上下文中的PlaceholderPropertyConfigurerreplaceconf值。 在这种情况下,您必须select正确的configuration,例如可以在构build时configuration。
与构build时间replace相比,如果需要,可以更轻松地临时操作未压缩的Web应用程序中的值。 当然,如果您更改了任何内容,则需要重新启动Web应用程序,并且手动更改不会在整个Web应用程序重新部署中持续。 Spring也仅限于Spring上下文,所以这不起作用,例如在web.xml中 (但是由于其局限性,web.xml中的variables应该可以避免)。
从预定义的文件中读取本地conf
这种方法可能是最简单的方法:创build一个configuration文件path,例如$HOME/mywebapp/conf.properties
,让你的webapp在启动时以某种方式读取。
这里的好处是,在构build/部署webapp时,您不必关心conf。 无论如何,你应该有一些明智的默认configuration,然后可以由本地configuration覆盖。
在数据库中有conf
这是覆盖conf参数的最灵活的解决scheme,但在某些情况下也会变得复杂。 在具有name
和value
列的表中configurationconf应该适用于大多数情况。
当然,你不能在数据库表中configurationJDBC连接URL,但是这对于简单的文本/数字conf来说是一个很好的解决scheme,它会在db连接build立后影响webapp的操作。 为了避免性能损失,请确保您cachingconfiguration文件,如果它会频繁访问。
额外的做法
正如kgiannakakis所指出的,它也有助于为您的应用程序设置某种configuration诊断页面。
这很大程度上取决于Web应用程序服务器提供的选项。 我们有多个不同JDBC URL的环境,JNDI名称在所有服务器上保持不变,只是本地实例上的configuration发生变化,所以从构build到构build都没有任何问题。
我想简单的回答是,最好的做法是将configuration外部化,并为每个服务器保持正确的设置,并让Web应用程序读取该configuration。 外部化和阅读的确切性质将取决于特定的configuration和应用程序服务器。
编辑:这些configuration不作为战争(耳在我们的情况下)的方式存在,他们不被覆盖。
起初有所有configuration设置在一个地方经常改变。 如果您需要同时设置JNDI,编辑数据库值和修改属性文件,那么确实很难完成configuration。 喜欢最容易编辑的媒体,也更容易validation一切设置正确。 我会说属性文件是最好的解决scheme。 你可以很容易地编辑它们,你只需要快速浏览一下,看看没有问题。 如果您select属性文件,请仔细为它们select一个标准位置,并为path分配一个环境variables。
如果您有一个简单的testing来validation所有设置是否正确,它也会有所帮助。 例如,您可以拥有一个显示configuration参数的testing页,并执行一些基本testing,例如尝试连接到数据库或远程服务器。
在Seam或Grails(从Rails借用)中使用你想要的很好的例子。 有configuration文件,默认三个:prod,开发,testing,但你可以定义更多,如果你想。
在Seam项目构build是由Ant文件完成的。 Eeach文件的内容可以改变定义为每个configuration文件,例如数据源,SQL脚本或属性文件。
import-dev.sql
import-prod.sql
import-test.sql
当selectconfiguration文件运行ant文件时,将采用适当的文件,并从该文件名中截断configuration文件名称。
以下是您可以放置在目标中的代码段
<copy tofile="${war.dir}/WEB-INF/classes/import.sql" file="${basedir}/resources/import-${profile}.sql"/>
JDBC URL,驱动程序名称可以外部化为属性文件(当然configuration文件名称为后缀)
<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>
或者你可以通过命令行构build调用的属性值。 这是Seam在做什么的简单例子。
另一个select是使用Maven 。 在Maven的方式,它由属性和configuration文件完成 ,但你也可以使用单独的模块拆分configuration和创build其他模块与主要function。 maven属性和configuration文件的典型用例示例是针对多个数据库,部署服务器等的运行configuration。当您想要为不同的供应商创buildconfiguration时更加困难,但对于Maven来说,这不是问题:)
使用mavenconfiguration文件的一个很好的例子是Carlos Sanchez的博客 。
总结一下,我强烈build议查找Ant / Seam Maven参数化(configuration文件)。 这些解决scheme还有另一个优点:可以在CI服务器(如Hudson )中运行ant或maven脚本,并让所有configuration文件同时运行/testing。
您可以使用您select的语言的组件configuration模式
这是描述在POSA书(我认为在第4卷)
(在Java中,您可以使用commons-configuration组件)。
有几种可能的方法来解决这个问题:
-
像你一样使用属性文件,但添加一个“元属性”文件,用于select通过定义环境值(例如本地主机名)到要加载的属性文件名之间的映射使用的属性文件。
-
把你的属性放到一个数据库中,并定义到你的应用程序服务器的属性表的数据库连接,作为你的web应用程序拾取的资源。
-
不要将属性文件放在.war或.ear文件中,而是要为每个目标主机创build一个包含属性文件的properties-deployhost.jar归档文件。 通过将相应的.jar文件添加到类path中(例如,通过应用程序服务器configuration中的每个web-app的共享库),将相应的.jar文件绑定到已部署的Web应用程序。
只有其中的第一个在部署时不需要额外的手动步骤,而需要更新configuration源并在重命名目标系统时构build新的部署文件。
我敢肯定,这些方面有很多的不同,你的方法是可能的,什么是最好的select取决于你的情况。
我们做的工作很好。
在启动时,我们的程序读取硬编码path中的configuration文件。 我们说这是:
/config/fun_prog/config.xml
每个程序都有一个不同的硬编码path(FunP轨程序在fun_prog中,超级服务器在sup_serv中,无论如何),所以我们不必担心他们彼此走过。
XML文件由我们创build的一个小configuration库读取。 XML文件包含数据库连接信息,通常邮件服务器configuration数据,发送通知的电子邮件地址,是否应以testing模式运行,外部服务的URL等。
所以当我们需要修改时,我们复制configuration文件,编辑我们想要的,然后重新启动程序。 由于我们有一个标准的服务器设置,只要复制这些文件(以及必要的httpd.conf修补),任何程序都可以部署在任何服务器上。
这不是幻想,但它工作得很好。 理解起来非常简单,添加新的configuration选项,备份和编辑。 适用于所有平台(unix是显而易见的,Windows将path以/开头转换为c:\,所以它在没有编辑的情况下也可以运行)。
我们的工作站基本上运行与服务器相同的软件,只是在该configuration文件中进行了一些更改。
请看这个url: http : //issues.apache.org/jira/browse/CONFIGURATION-394
我们正在寻找它的configuration框架是在Apache Commonsconfiguration之上,并且必须支持并发问题,JMX问题和大多数商店(例如.properties文件,.xml文件或者PreferencesAPI)。
weblogic团队在“pipe理控制台”上提供的是通过它可以对configuration进行交易(primefaces)更新,以便通知注册的监听者。
Apache的人坚持说这个项目已经超出了Commons Configuration的范围,也许吧!
我附上了一个简单的configuration框架,请看看。