您如何管理 java webapps 中的嵌入式配置文件和库?

Posted

技术标签:

【中文标题】您如何管理 java webapps 中的嵌入式配置文件和库?【英文标题】:How do you manage embedded configuration files and libraries in java webapps? 【发布时间】:2010-10-09 14:17:53 【问题描述】:

我目前正在开发一个已经处于测试阶段的 j2ee 项目。现在,我们只是在解决部署过程中的一些问题。具体来说,战争中嵌入了许多文件(一些 xml 文件和 .properties),根据您是在开发、测试还是生产环境中,需要部署不同的版本。诸如日志级别、连接池等的东西。

所以我想知道这里的开发人员如何构建他们部署 web 应用程序的流程。您是否将尽可能多的配置卸载到应用程序服务器?您是否在部署之前以编程方式替换设置文件?在构建过程中选择一个版本?手动编辑战争?

另外,您在通过应用服务器的静态库提供依赖项方面走了多远,您自己在战争中投入了多少?所有这些只是为了了解目前的常见(或者可能是最佳)做法。

【问题讨论】:

【参考方案1】:

wrt 配置文件,我认为Steve's 的答案是迄今为止最好的。我会添加使外部文件相对于 war 文件的安装路径的建议 - 这样您就可以在一台具有不同配置的服务器上安装多个 war。

例如如果我的dev.war 被解压到/opt/tomcat/webapps/dev,那么我将使用ServletContext.getRealPath 来查找基本文件夹和war 文件夹名称,那么配置文件将存在于../../config/dev 相对于战争,或/opt/tomcat/config/dev绝对的。

我也同意Bill 关于在这些外部配置文件中添加尽可能少的内容。根据您的环境使用数据库或 JMX 来存储尽可能多的数据。 Apache Commons Configuration 有一个 nice object 用于处理由数据库表支持的配置。

关于库,我同意unknown 将所有库放在war 文件(自打包)中的WEB-INF/lib 文件夹中。优点是应用程序的每次安装都是自主的,您可能会同时使用不同版本的库来构建不同的战争。

缺点是它会使用更多内存,因为每个 Web 应用程序都有自己的类副本,由其自己的类加载器加载。

如果这引起了真正的担忧,那么您可以将 jar 放在您的 servlet 容器的公共库文件夹中($CATALINA_HOME/lib 用于 tomcat)。但是,在同一台服务器上运行的所有 Web 应用程序安装都必须使用相同版本的库。 (实际上,这并不完全正确,因为您可以在必要时将覆盖版本放在单独的 WEB-INF/lib 文件夹中,但维护起来非常麻烦。)

在这种情况下,我将为常用库构建一个自动安装程序,使用 InstallShield 或 NSIS 或您的操作系统的等效项。可以轻松判断您是否拥有最新的库集,以及升级、降级等。

【讨论】:

【参考方案2】:

我在一个单独的服务器团队为我们的应用程序执行 QA 和生产服务器配置的环境中工作。每个应用程序通常部署在 QA 中的两台服务器和生产中的三台服务器上。我的开发团队发现,最好通过将尽可能多的配置放入战争(或耳朵)中来最大限度地减少服务器上所需的配置量。这使得服务器配置更容易,也将服务器团队错误配置服务器的可能性降到最低。

我们没有特定于机器的配置,但我们有特定于环境的配置(开发、QA 和生产)。我们将配置文件存储在按环境命名的 war 文件中(例如 dev.properties、qa.properties、prod.properties)。我们在服务器 VM 的 java 命令行上放置了一个 -D 属性来指定环境(例如 java -Dapp.env=prod ...)。应用程序可以查找 app.env 系统属性并使用它来确定要使用的属性文件的名称。

我想如果您有少量特定于机器的属性,那么您也可以将它们指定为 -D 属性。 Commons Configuration 提供了一种将属性文件与系统属性结合起来的简单方法。

我们在服务器上配置连接池。我们为每个环境命名相同的连接池,并将分配给每个环境的服务器指向适当的数据库。应用程序只需要知道一个连接池名称。

【讨论】:

大部分答案都是关于说同样的话,接受这一点,因为我们最终可能会做一些非常相似的事情。【参考方案3】:

我一般会做两个属性文件:

一个用于嵌入在应用中的应用细节(消息、内部“神奇”词), 另一个用于环境细节(数据库访问、日志级别和路径...)在每个服务器的类路径上公开并“固定”(未随我的应用程序提供)。通常我会“mavenise”或“anttise”这些以放置特定值,具体取决于目标环境。 很酷的人使用 JMX 来维护他们的应用程序配置(配置可以实时修改,无需重新部署),但它对我的需求来说太复杂了。

服务器的(静态?)库:我强烈反对在我的应用程序中使用服务器库,因为它会增加对服务器的依赖:

    IMO,我的应用程序必须“自行打包”:放弃我的战争,仅此而已。我见过有 20 Mbs jar 的战争,这对我来说并不令人不安。 一个常见的最佳实践是将您的外部依赖限制在 J2EE 教条所提供的范围内:J2EE API(使用 Servlet、Ejbs、Jndi、JMX、JMS...)。您的应用必须“与服务器无关”。 将依赖项放入您的应用程序(war、ear、wathever)是自记录的:您知道您的应用程序依赖于哪些库。对于服务器库,您必须清楚地记录这些依赖关系,因为它们不太明显(很快您的开发人员就会忘记这个小魔法)。 如果您升级您的应用服务器,您所依赖的服务器库也可能会发生变化。 AppServer 编辑器不应该在其内部库上保持版本与版本之间的兼容性(而且大多数时候,他们不会)。 如果您使用嵌入在您的 appServer 中的广泛使用的 lib(想到 jakarta commons logging,又名 jcl)并且想要升级它的版本以获得最新功能,您将承担 appServer 不支持的巨大风险它。 如果您依赖静态服务器对象(在服务器类的静态字段中,例如 Map 或日志),则必须重新启动应用服务器以清除此对象。您失去了热重新部署应用程序的能力(旧的服务器对象在重新部署之间仍然存在)。使用 appServer 范围的对象(J2EE 定义的对象除外)可能会导致细微的错误,特别是如果该对象在多个应用程序之间共享时。这就是为什么我强烈反对使用驻留在 appServer 库的静态字段中的对象。

如果您绝对需要“此应用服务器的 jar 中的此对象”,请尝试将 jar 复制到您的应用中,希望不依赖其他服务器的 jar,并检查您的应用的类加载策略(我习惯放置一个“父我所有应用程序的最后一个“类加载策略:我确信我不会被服务器的 jars “污染” - 但我不知道这是否是“最佳实践”)。

【讨论】:

【参考方案4】:

我把所有的配置都放到了数据库里。容器(Tomcat、WebSphere 等)让我可以访问初始数据库连接,从那时起,一切都来自数据库。这允许多个环境、集群和动态更改而无需停机(或至少无需重新部署)。特别好的是能够动态更改日志级别(尽管您需要管理屏幕或后台刷新来获取更改)。显然,这仅适用于启动应用程序不需要的东西,但通常,您可以在启动后很快进入数据库。

【讨论】:

【参考方案5】:

我认为如果属性是特定于机器/部署的,那么它们属于机器。如果我要在战争中结束一切,它应该是可放置的,这意味着它没有特定于运行它的机器。如果战争中有机器相关的属性,这个想法就会被打破。

我喜欢用 properties.example 文件构建一个项目,每台机器都有一个 .properties,它位于战争可以访问的地方。

另一种方法是使用 ant 任务,例如对于 dev-war、stage-war、prod-war 和项目的属性集部分,在 war-build 中烘焙。我不太喜欢这样,因为作为项目构建的一部分,您最终会在单个服务器上拥有诸如文件位置之类的东西。

【讨论】:

以上是关于您如何管理 java webapps 中的嵌入式配置文件和库?的主要内容,如果未能解决你的问题,请参考以下文章

您如何在不同的暂存环境中维护 java webapps?

Azure 服务结构资源管理器中的 Java Webapp 部署

如何在 Amazon EC2 上部署 Java webapp,以便您可以有效地使用自动缩放?

如何在 Amazon EC2 上部署 Java webapp,以便您可以有效地使用自动缩放?

对使用 java -jar 部署的 Grails 3 webapp (Springboot) 应用程序使用 Logback 访问

如何让 WebApp 在嵌入式码头的类路径上找到应用程序属性?