【问题标题】:Managing configuration in Erlang application在 Erlang 应用程序中管理配置
【发布时间】:2011-09-07 13:12:06
【问题描述】:

我需要通过我的应用程序分发某种静态配置。这样做的最佳做法是什么?

我看到三个选项:

  1. 当模块需要获取配置值时直接调用application:get_env
    • 另外:比其他选项更简单。
    • 减号:如何在不启动整个应用程序的情况下测试此类模块?
    • 减号:如何启动具有不同配置的某些模块(如果需要)?
  2. 在启动期间将配置(从application:get_env 检索)传递给应用程序模块。
    • 另外:模块更容易测试,您可以使用不同的配置启动它们。
    • 减号:大量样板代码。更改配置格式需要修复几个地方。
  3. 在单独的配置过程中保留配置。
    • 另外:或多或少的类型安全方法。更容易跟踪某些参数的使用位置和更改这些位置。
    • 减号:需要在运行模块之前启动配置过程。
    • 减号:如何启动具有不同配置的某些模块(如果需要)?

【问题讨论】:

  • 您是否考虑过以编程方式启动节点的含义(例如,使用 ct_slave)?更改 .app 或 .config 文件很麻烦,使用“静态”模块方法是不切实际的……我仍在努力找出最好的方法……
  • 我必须(或我的猫​​)错误地投票否决了这个,现在显然我的决定被锁定了......对不起。

标签: configuration erlang


【解决方案1】:

另一种方法是将您的配置数据转换为 Erlang 源模块,使配置数据可通过导出获得。然后,您可以在正在运行的系统中随时更改配置,只需加载新版本的配置模块即可。

【讨论】:

  • 好主意,谢谢!唯一需要担心的是,出于测试目的,我需要几个具有相同名称的模块,这可能会造成混淆。
  • 您可以在测试期间使用meck 创建模拟模块(无耻的自我插件,我是作者),或者在您的代码中参数化模块名称(这对于测试来说非常简洁)。
  • 现有哪些库可以将您的配置数据转换为 Erlang 源代码?
  • @Habibutsu:我不知道有任何库或工具可以做到这一点。我的意思是手动将您的配置转换为适合您的 Erlang 应用程序的配置。
【解决方案2】:

对于我自己项目中的静态配置,我喜欢选项 (1)。我将向您展示我在名为 factory 的应用程序中访问名为 max_widgets 的配置参数所采取的步骤。

首先,我们将创建一个名为 factory_env 的模块,其中包含以下内容:

-define(APPLICATION, factory).

get_env(Key, Default) ->
    case application:get_env(?APPLICATION, Key) of
        {ok, Value} -> Value;
        undefined -> Default
    end.

set_env(Key, Value) ->
    application:set_env(?APPLICATION, Key, Value).

接下来,在需要读取max_widgets 的模块中,我们将定义如下宏:

-define(MAX_WIDGETS, factory_env:get_env(max_widgets, 1000)).

这种方法有一些好处:

  • 因为我们使用了application:set_env/3application:get_env/2,我们实际上不需要启动factory 应用程序来通过我们的测试。
  • max_widgets 获取默认值,因此即使未定义参数,我们的代码仍然可以工作。
  • 第二个模块可以为 max_widgets 使用不同的默认值。

最后,当我们准备好部署时,我们将在我们的priv 目录中放置一个sys.config 文件,并在启动时使用-config priv/sys.config 加载它。如果需要,这允许我们在每个节点的基础上更改配置参数。这清楚地将配置与代码分开 - 例如我们不需要再次提交即可将 max_widgets 更改为 500。

【讨论】:

  • 嗯,我很确定你不能在没有启动应用程序的情况下获取_env/set_env。谢谢!
  • 应用程序必须加载,但不需要启动。
  • application:get_env(?APPLICATION, Key, undefined),很抱歉唤醒了一个旧帖子,但您可以使用它来返回默认值
【解决方案3】:

您可以使用进程(可能是 gen_server?)将配置参数存储在其状态中。它应该公开一个 get/set 接口。如果一个值没有被显式设置,它应该检索一个默认值。

-export([get/1, set/2]).

...

get(Param) ->
  gen_server:call(?MODULE, {get, Param}).

...

handle_call({get, Param}, _From, State) ->
  case lookup(Param, State#state.params) of
    undefined ->
      application:get_env(...);
    Value ->
      {ok, Value}
  end.

...

然后,您可以轻松地在测试中模拟此模块。在运行时使用一些新配置更新流程也很容易。

您可以使用模式匹配和元组将不同的配置参数关联到不同的模块:

set({ModuleName, ParamName}, Value) ->
  ...

get({ModuleName, ParamName}) ->
  ...

将进程放在监督树下,以便在需要配置的所有其他进程之前启动它。

哦,我很高兴到目前为止没有人建议 parametrized modules :)

【讨论】:

  • 这应该是这个问题的答案
【解决方案4】:

我会为静态配置执行选项 1。您始终可以通过application:set_env/3,4 设置选项进行测试。您想要这样做的原因是您的应用程序测试需要在某个时候运行整个应用程序。并且此时设置特定于测试的配置的能力非常好。

应用程序控制器默认运行,因此您需要以应用程序方式运行不是问题(无论如何您也需要这样做!)

最后,如果一个进程需要特定的配置,在配置数据中说清楚!您可以存储任何 Erlang 术语,特别是,您可以存储一个术语,使您能够覆盖特定节点的配置参数。

对于动态配置,您最好使用gen_server 或使用最新的gproc 功能来存储此类动态配置。

【讨论】:

    【解决方案5】:

    我还看到人们使用 .hrl(erlang 头文件),其中定义了所有配置,并将其包含在任何需要配置的文件的开头。

    它可以进行非常简洁的配置查找,并且您可以获得任意复杂的配置。

    我相信您也可以在运行时通过执行模块的热代码重新加载来重新加载配置。缺点是如果您在多个模块中使用配置并仅重新加载其中一个,则只有一个模块会更新其配置。

    但是,我实际上并没有检查它是否像那样工作,而且我找不到关于 .hrl 和热代码重新加载如何交互的权威文档,因此请务必仔细检查在你真正使用它之前。

    【讨论】:

      猜你喜欢
      • 2012-12-10
      • 2012-07-05
      • 1970-01-01
      • 1970-01-01
      • 2011-07-11
      • 1970-01-01
      • 2011-03-07
      • 1970-01-01
      • 2018-05-14
      相关资源
      最近更新 更多