将运行时配置传递给 elixir 进程的正确方法

Correct way to pass runtime configuration to elixir processes

我正在尝试将应用程序部署到生产环境,但对环境和应用程序变量以及编译时和运行时发生的情况感到有些困惑。

在我的应用程序中,我有一个需要令牌才能运行的 genserver 进程。所以我使用 config/releases.exs 在运行时设置令牌变量:

# config/releases.exs

import Config

config :my_app, :my_token, System.fetch_env!("MY_TOKEN")

然后我有一些代码看起来有点像这样:

defmodule MyApp.SomeService do

  use SomeBehaviour, token: Application.get_env(:my_app, :my_token),
                     other_config: :stuff

  ...

end

在生产中,genserver 进程(执行一些 http 操作)给我 403 错误提示令牌不存在。那么我可以澄清一下,use 关键字是否在编译时得到评估(在这种情况下,应用程序环境还不存在)?

如果是这样,将运行时环境变量获取到这样的服务中的正确方法是什么。启动进程时在 application.ex 中定义配置是否更正确?例如

children = [
  {MyApp.SomeService, [
    token: Application.get_env(:my_app, :my_token),
    other_config: :stuff
  ]}
  ...
]

Supervisor.start_link(children, opts)

我可能已经在这里回答了我自己的问题,但是让知道他们在做什么的人确认并指出我正确的方法会很有帮助。谢谢

有两个阶段:编译和运行时,都写在 Elixir 本身。为了清楚地理解当人们应该弄清楚时会发生什么,一切都是宏Elixir,在编译阶段,扩展这些宏直到一切都被扩展。该 AST 进入运行时。

在您的示例中,use SomeBehaviour, foo: :bar 隐式调用 SomeBehaviour.__using__/1 宏。要扩展 AST,还需要扩展参数(关键字列表)。因此,Application.get_env(:my_app, :my_token) 调用发生在编译时。

将其移至运行时有多种可能性。如果您是 SomeBehaviour 的所有者,请让它接受 {:my_app, :my_token} 对并从其中的某处调用 Application.get_env/2

或者,按照你的建议,将其作为参数传递给children;此代码属于函数体,这意味着它不会在编译阶段尝试扩展,而是作为 AST 传递给生成的 BEAM,以便在运行时执行。