java 我可以在多个环境中使用单个 war 文件吗?我是不是该?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1634458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 17:21:53  来源:igfitidea点击:

Can I use a single war file in multiple environments? Should I?

javajspjakarta-ee

提问by NobodyMan

I have a Java web application at my work and I'd like simplify how we deploy to our DEV, QA, and PROD environments.

我的工作中有一个 Java Web 应用程序,我想简化我们部署到我们的 DEV、QA 和 PROD 环境的方式。

The application reads in a series of properties at startup, and the properties files are different for dev, qa, and prod. Whenever I want to deploy to a certain environment I drop the environment-specific properties file into my app folder, build the war, and then deploy it to one of the three tomcat 5.5 servers.

应用程序在启动时读入一系列属性,dev、qa、prod的属性文件是不同的。每当我想部署到某个环境时,我都会将特定于环境的属性文件放入我的应用程序文件夹中,构建War,然后将其部署到三个 tomcat 5.5 服务器之一。

What I would like to do is have to have a single .war that has the properties for all environments, and have the app interrogate the webserver during the init process to figure out which environment the app is in, and hence which properties to load. Is there an easy way (or, failing that, a standard way) to do that?

我想要做的是拥有一个具有所有环境属性的 .war 文件,并让应用程序在 init 过程中询问网络服务器以确定应用程序所在的环境,从而确定要加载的属性。有没有一种简单的方法(或者,如果失败了,一种标准的方法)来做到这一点?

采纳答案by ChssPly76

This really depends on what you are using those properties for.

这实际上取决于您使用这些属性的目的。

Some (like data source, for example) can be configured in the container itself (Tomcat 5.5. JNDI Resources, see JDBC sources section as well).

一些(例如数据源)可以在容器本身中配置(Tomcat 5.5.JNDI 资源,参见 JDBC 源部分)。

Others (application-specific) may indeed need to be properties. In which case your choices are:

其他(特定于应用程序)可能确实需要是属性。在这种情况下,您的选择是:

  1. Bundle properties within WAR file and load the appropriate subset based on some external switch (either environment variable or JVM property)
  2. Setup a deployment process on each of your servers where war is unpacked and a property file (located in a predefined location on that server and specific to that server) is copied over to WEB-INF/classes(or other appropriate place).
  1. 捆绑 WAR 文件中的属性并根据某些外部开关(环境变量或 JVM 属性)加载适当的子集
  2. 在解压 war 的每个服务器上设置部署过程,并将属性文件(位于该服务器上的预定义位置并特定于该服务器)复制到WEB-INF/classes(或其他适当的位置)。

As far as "is this a desirable goal" goes - yes, I think so. Having a single WAR to test in QA / staging and then deploy to production cuts out an intermediate step and thus leaves less chances for mistakes.

至于“这是一个理想的目标吗”——是的,我认为是的。有一个单一的 WAR 在 QA / staging 中进行测试,然后部署到生产中,可以减少中间步骤,从而减少出错的机会。

Update(based on comment):

更新(基于评论):

Item #1 above refers to an actual environment variable (e.g. something that you set via SET ENV_NAME=QAin Windows or ENV_NAME=QA; export ENV_NAMEin Linux). You can the read its value from your code using System.getenv()and load the appropriate properties file:

上面的第 1 项是指实际的环境变量(例如,您SET ENV_NAME=QA在 Windows 或ENV_NAME=QA; export ENV_NAMELinux 中通过设置的内容)。您可以使用System.getenv()并加载适当的属性文件从代码中读取其值:

String targetEnvironment = System.getenv("TARGET_ENV");
String resourceFileName = "/WEB-INF/configuration-" + targetEnvironment + ".properties";
InputStream is = getServletContext().getResourceAsStream(resourceFileName);
Properties configuration = new Properties();
configuration.load(is);

But yes, you can instead define a scalar value via JNDI (see Environment Entries in Tomcat doc) instead:

但是,是的,您可以改为通过 JNDI 定义标量值(请参阅 Tomcat 文档中的环境条目):

<Context ...>
  <Environment name="TARGET_ENV" value="DEV" type="java.lang.String" override="false"/>
</Context>

and read it within your app via

并通过在您的应用程序中阅读它

Context context = (Context) InitialContext().lookup("java:comp/env");
String targetEnvironment = (String) context.lookup("TARGET_ENV");
// the rest is the same as above

The thing is, if you will be using JNDI anyway, you might as well forgo your property files and configure everything via JNDI. Your data sources will be available to you as actual resources and basic properties will remain scalars (though they will be type safe).

问题是,如果您无论如何都将使用 JNDI,那么您最好放弃您的属性文件并通过 JNDI 配置所有内容。您的数据源将作为实际资源可供您使用,基本属性将保持标量(尽管它们将是类型安全的)。

Ultimately it's up to you to decide which way is better for your specific needs; both have pros and cons.

最终由您决定哪种方式更适合您的特定需求;两者都有优点和缺点。

回答by fvu

What you do is an accident waiting to happen... One day a DEV war will end up in de PROD server, and by some law superior to all laws of nature that problem will be detected at 2AM. Can't explain why this is the case, but some day that will happen. So one war is in my opinion definitely a good idea.

你所做的是一场等待发生的事故......有一天,一场 DEV War将在 de PROD 服务器中结束,并且根据某种优于所有自然法则的法则,这个问题将在凌晨 2 点被检测到。无法解释为什么会这样,但总有一天会发生。所以在我看来,一场War绝对是个好主意。

You can set a system property in the respective JVM's (-Dcom.yourdomain.configpath=/where/you/store/configfiles) and fetch this property with

您可以在相应的 JVM 中设置系统属性 (-Dcom.yourdomain.configpath=/where/you/store/configfiles) 并使用

String value = System.getProperty("com.yourdomain.configpath", "defaultvalue_if_any"); 

The default value could point somewhere inside the war (WEB-INF/...), or if there's no default, be used to make some logging noise during load to warn for misconfiguration). Also note that this technique is not platform dependent, so you dev machine can be a Windows box and the server a Linux machine, it can cope with both. We normally create a subdir per application in this configpath, as several applications use this system on a server, and we want to keep things tidy.

默认值可能指向War内部的某个地方(WEB-INF/...),或者如果没有默认值,则用于在加载期间产生一些日志记录噪音以警告错误配置)。另请注意,此技术不依赖于平台,因此您的开发机器可以是 Windows 机器,服务器可以是 Linux 机器,它可以同时处理两者。我们通常在这个配置路径中为每个应用程序创建一个子目录,因为有几个应用程序在服务器上使用这个系统,我们希望保持整洁。

As an added bonus, you don't risk to trash manually tweaked property files on a PROD server this way. Just don't forget to include the path where the files are stored in a backup scenario.

作为一个额外的好处,您不会冒险以这种方式在 PROD 服务器上手动调整属性文件。只是不要忘记包含文件在备份方案中的存储路径。

回答by Spike Williams

I think a single war file is a good way to go, because its nice to have confidence that the binary you tested in DEV is exactly the same as in Production. The way we do it, we keep the configurations in a separate properties file, outside the war, but in the app server's class path.

我认为单个 war 文件是一个很好的方法,因为很高兴确信您在 DEV 中测试的二进制文件与生产中的二进制文件完全相同。我们这样做的方式是,我们将配置保存在单独的属性文件中,在 war 之外,但在应用程序服务器的类路径中。

If you want to keep all the properties inside the war (which does make deployment easier, because then you don't have to also deploy a properties file), you could keep a single properties file in the classpath that identifies the server environment type, and use that to key values in the properties file within your .war file. The external properties file may also be a good way to go for maybe some high-level configurations that don't change much and are used across a number of war files.

如果您想将所有属性保留在War中(这确实使部署更容易,因为您不必同时部署属性文件),您可以在标识服务器环境类型的类路径中保留一个属性文件,并使用它来键入 .war 文件中属性文件中的值。外部属性文件也可能是一个很好的方法,用于一些没有太大变化并在许多War文件中使用的高级配置。

回答by Brian

I prefer the one EAR or one WAR approach. There is something re-assuring and often required from a security standpoint about taking the exact same EAR that was just tested and moving it directly into the next state (test > Production).

我更喜欢一种 EAR 或一种 WAR 方法。从安全的角度来看,有一些令人放心的事情,并且通常需要采取与刚刚测试过的完全相同的 EAR 并将其直接移动到下一个状态(测试 > 生产)。

There are also many options besides properties files provided by the container. Often the container has a nice UI to maintain those values and resources when you are dead and gone.

除了容器提供的属性文件之外,还有很多选项。通常,容器有一个很好的 UI 来在您死后维护这些值和资源。

There are countless examples of using a database backed ResourceBundle.

有无数使用数据库支持的ResourceBundle 的例子。

For example, the Spring framework has several mechanisms to make that happen without much effort. Starting with PropertyPlaceholderConfigurer

例如,Spring 框架有多种机制可以轻松实现这一点。从PropertyPlaceholderConfigurer开始

回答by broschb

Set a Systemproperty at startup that points to the location of your properties file, and then in your application pull in this property and load your settings. Another thing I do is have two properties file, something like default.properties, and external.properties. They contain the same properties, but the default.properties contains the default(works most of the time settings), this file goes in the war. Then if you deploy to an env. you look for the external.properties, if found that is used, if not then you rollback to the default.properties. This provides a nice way to override properties if needed, but also have a default setup. This works in a lot of my deployments, but may not in your scenario.

在启动时设置一个 Systemproperty 指向您的属性文件的位置,然后在您的应用程序中拉入该属性并加载您的设置。我做的另一件事是有两个属性文件,比如 default.properties 和 external.properties。它们包含相同的属性,但 default.properties 包含默认值(大部分时间都可以使用设置),这个文件在War中。然后,如果您部署到 env。你寻找external.properties,如果找到它被使用,如果没有那么你回滚到default.properties。如果需要,这提供了一种覆盖属性的好方法,但也有一个默认设置。这适用于我的许多部署,但可能不适用于您的场景。

回答by Chris

Probably the simplest way would be to set up an environment variable that differs between the application services and use this to determine which property file to read.

可能最简单的方法是设置一个在应用程序服务之间不同的环境变量,并使用它来确定要读取的属性文件。

Another possibility would be to store the properties in a database, and use a datasource that exists under a standard JNDI name, but points to a different place in the various environments.

另一种可能性是将属性存储在数据库中,并使用以标准 JNDI 名称存在但指向各种环境中不同位置的数据源。

回答by Prakash

A single build (war) is certainly the right approach. However, when it comes to environment specific configuration, the best way to go is to ensure that all configuration .properties files should not be pushed to all the environments. e.g. PROD properties files should be copied to DEV or UAT. Spring profiles also should be avoided as they lead to convoluted configuration management.

单一构建(War)当然是正确的方法。但是,当涉及到特定于环境的配置时,最好的方法是确保不应将所有配置 .properties 文件推送到所有环境。例如,应该将 PROD 属性文件复制到 DEV 或 UAT。Spring 配置文件也应该避免,因为它们会导致复杂的配置管理。

回答by Kaleb Brasee

Absolutely a single WAR is the best way to go. Set the resources using the same JNDI names in each environment, and if you need to detect which environment you're in for business logic purposes, use a System property on Tomcat startup.

绝对单一的War是最好的方式。在每个环境中使用相同的 JNDI 名称设置资源,如果出于业务逻辑目的需要检测您所在的环境,请在 Tomcat 启动时使用系统属性。