Scala中的环境特定配置

时间:2014-02-06 15:56:52

标签: scala configuration environment-variables config development-environment

在Scala中设置项目的好方法是什么,它根据环境使用不同的配置。

我需要为开发测试生产环境专门设置不同的数据库(类似于Rails中的操作)

4 个答案:

答案 0 :(得分:42)

我正在使用的另一个策略是使用includes。 我通常将我的DEV设置存储在default application.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}
}

请注意,我仅覆盖 在PROD环境中更改的设置(some-other-setting因此与DEV中相同)。

配置引导程序代码不会测试任何内容

...
val conf = ConfigFactory.load()
...

要从DEV切换到PROD conf,只需传递一个带有配置文件名称的系统属性即可:

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

在DEV中,由于default将加载application.conf,因此无需传递。

所以我们在这里使用Typesafe Config的默认加载机制来实现这一点。

我创建了一个简单的project来演示这种技术。随意克隆和实验。

答案 1 :(得分:20)

使用typesafe配置。像这样创建一个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)
}

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

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"
  }
}

现在,您可以从应用程序的任何位置访问配置:

配置()。的getString( “your_app.databaseUrl”)

如果您在运行应用程序时设置了环境(例如export SCALA_ENV=test),它将考虑正确的配置部分。默认为开发

答案 2 :(得分:2)

我对Daniel Cukiers解决方案不允许默认和覆盖的方式感到不满意,所以我改变它以充分利用它们。

您需要做的唯一配置是在系统上设置一个ENVIRONMENT变量(如果没有设置,则默认为'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")

答案 3 :(得分:0)

这是Scala中的解决方案,允许覆盖,并且不依赖于外部库。

object Config {

  var test: Map[String, String] = {
    Map(
      "libsvmData" -> new java.io.File("./src/test/resources/sample_libsvm_data.txt").getCanonicalPath,
      "somethingElse" -> "hi"
    )
  }

  var production: Map[String, String] = {
    Map(
      "libsvmData" -> "s3a://my-cool-bucket/fun-data/libsvm.txt",
      "somethingElse" -> "whatever"
    )
  }

  var environment = sys.env.getOrElse("PROJECT_ENV", "production")

  def get(key: String): String = {
    if (environment == "test") {
      test(key)
    } else {
      production(key)
    }
  }

}

如果PROJECT_ENV设置为testConfig.get("somethingElse")将返回"hi"。否则,它将返回"whatever"

运行PROJECT_ENV=test sbt test将变得很快,因此您可以让SBT在运行测试套件时设置环境变量。

fork in Test := true
envVars in Test := Map("PROJECT_ENV" -> "test")

以下是如何覆盖现有配置。

Config.test = Config.test ++ Map("somethingElse" -> "give me clean air")

我在a link to the full blog post写了这个话题。