Scala中的按环境设置的特定配置

36

如何在Scala中设置一个良好的项目,以便根据不同环境使用不同的配置。

我需要针对开发测试生产环境具有不同的数据库(类似于Rails中所做的)。

3个回答

69
我将使用另一种策略,即使用 includes。通常我会将我的 DEV 设置存储在 defaultapplication.conf 文件中,然后为其他环境创建一个新的 conf 文件并包含默认文件。
假设我的 DEV conf application.conf 如下所示:
myapp {
    server-address = "localhost"
    server-port = 9000

    some-other-setting = "cool !"
}

那么对于生产环境(PROD),我可以有另一个名为prod.conf的文件:

include "application"

# override default (DEV) settings
myapp {
    server-address = ${PROD_SERVER_HOSTNAME}
    server-port = ${PROD_SERVER_PORT}
}

请注意,我仅覆盖在生产环境中更改的设置(因此some-other-setting与DEV中相同)。
配置引导代码不测试任何内容。
...
val conf = ConfigFactory.load()
...

要从DEV切换到PROD配置,只需传递一个系统属性,该属性的名称为要加载的配置文件的名称:

java -Dconfig.resource=prod.conf ...

在DEV中,不需要传递它,因为application.conf将由default加载。
因此,我们使用Typesafe Config的默认加载机制来实现这一点。
我创建了一个简单的项目来演示这种技术。随意克隆和尝试。

为了完整起见,我认为配置引导应该是:val conf = ConfigFactory.load() def apply() = conf.getConfig("myapp") - mmeyer
@ozeebee,出于某种原因,这不起作用。请注意,我在build.sbt中添加了:fork in run := truejava -Dconfig.resource=stg.conf。文件stg.conf位于资源下,但我的配置仍然来自application.conf。还要注意,当我使用:val conf = ConfigFactory.load("stg.conf")时,我的配置是按照stg.conf加载的。有什么想法吗? - has981
@has981 没有头绪。我已经建立了一个简单的项目来演示这个技术。请随意克隆和尝试。 - ozeebee
@ozeebee,你的代码在本地运行良好,但是当我将这段代码部署到使用Docker和Kubernetes的Flink上时,它无法正常工作。 - Kallz
1
@Kallz 抱歉,我不了解flink;也许这个工具有自己的配置机制? - ozeebee

30

使用类型安全的 Config。创建 Config 对象的方式如下:

import com.typesafe.config._

object Config {
  val env = if (System.getenv("SCALA_ENV") == null) "development" else System.getenv("SCALA_ENV")

  val conf = ConfigFactory.load()
  def apply() = conf.getConfig(env)
}

然后在 src/main/resources 文件夹中创建 application.conf 文件:

development {
  your_app {
    databaseUrl = "jdbc:mysql://localhost:3306/dev_db"
    databaseUser = "xxxx"
    databasePassword = "xxxx"
  }
}
test {
  your_app {
    databaseUrl = "jdbc:mysql://localhost:3306/test_db"
    databaseUser = "xxxxx"
    databasePassword = "xxxx"
  }
}

现在,你可以在应用程序的任何位置访问配置文件:

Config().getString("your_app.databaseUrl")

如果你已经设置好了环境变量(例如export SCALA_ENV=test),当你运行应用程序时,它将考虑正确的配置部分。默认是开发环境。


我花了一些时间才找到这个模式,它保持了系统属性的优先级:ConfigFactory.defaultOverrides().withFallback(config2.getConfig(environment)).withFallback(config2)。https://github.com/typesafehub/config/blob/master/examples/java/complex-app/src/main/java/ComplexApp.java - David W

2

我对Daniel Cukiers的解决方案不满意,因为它不允许使用默认值和覆盖值,所以我做了一些改动来充分利用它们。

您唯一需要配置的是在系统上设置一个环境变量(如果没有设置,则默认为“dev”)

(Java解决方案,与Scala兼容):

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

public class MyCompanyConfig {
    public final static Config base = ConfigFactory.load().getConfig("mycompany");
    public final static String environment = System.getenv("ENVIRONMENT") == null ? "dev" : System.getenv("ENVIRONMENT");

    /**
     * Returns a subtree of the base configuration with environment settings applied.
     *
     * @param setting The subtree to return config for.
     * @return A config with base in given setting, with environment modifications applied.
     */
    public static Config load(String setting) {

        Config config = base.getConfig(setting);

        if (config.hasPath(environment)) {
            return config.getConfig(environment).withFallback(config);
        }

        return config;
    }
}

这使得一个库中的单个reference.conf文件看起来像这样:
mycompany.module1 {
    setting1 : "adefaultvalue"
    url : "localhost"

    test {
        // will be used where ENVIRONMENT="test"
        url : "test.mycompany.com"
    }

    prod {
        // will be used where ENVIRONMENT="prod"
        setting1 : "changethedefault"
        url : "www.mycompany.com"
    }
}

使用方法:

Config conf = MyCompanyConfig.load("module1")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接