Управление конфигурацией в приложении Erlang
Мне нужно распространять некоторую статическую конфигурацию через мое приложение. Какова наилучшая практика?
Я вижу три варианта:
- Вызовите
application:get_env
напрямую, когда требуется, чтобы модуль получил значение конфигурации.
- Плюс: проще других.
- Минус: как протестировать такие модули, не доведя все приложение?
- Минус: как запустить определенный модуль с другой конфигурацией (если требуется)?
- Передайте конфигурацию (извлеченную из
application:get_env
), в прикладные модули во время запуска.
- Плюс: модули легче тестировать, вы можете запускать их с другой конфигурацией.
- Минус: много шаблонов. Изменение формата конфигурации требует фиксации нескольких мест.
- Удерживайте конфигурацию в отдельном процессе настройки.
- Плюс: более или менее безопасный тип. Легче отслеживать, где используется определенный параметр, и изменять эти места.
- Минус: нужно запустить процесс настройки перед запуском модулей.
- Минус: как запустить определенный модуль с другой конфигурацией (если требуется)?
Ответы
Ответ 1
Другой подход заключается в преобразовании данных конфигурации в исходный модуль Erlang, который делает данные конфигурации доступными через экспорт. Затем вы можете изменить конфигурацию в любой момент в запущенной системе, просто загрузив новую версию модуля конфигурации.
Ответ 2
Для статической конфигурации в моих собственных проектах мне нравится опция (1). Я покажу вам шаги, которые я предпринимаю для доступа к параметру конфигурации с именем max_widgets
в приложении под названием factory
.
Сначала мы создадим модуль под названием 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/3
и application:get_env/2
, нам действительно не нужно запускать приложение factory
для прохождения наших тестов.
-
max_widgets
получает значение по умолчанию, поэтому наш код будет работать, даже если параметр не определен.
- Второй модуль может использовать другое значение по умолчанию для
max_widgets
.
Наконец, когда мы будем готовы к развертыванию, мы поместим файл sys.config в наш каталог priv
и загрузим его -config priv/sys.config
во время запуска. Это позволяет нам при необходимости изменить параметры конфигурации на основе node. Это чисто отделяет конфигурацию от кода - например, нам не нужно делать еще одну фиксацию, чтобы изменить max_widgets
на 500.
Ответ 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}) ->
...
Поместите процесс под деревом надзора, чтобы он запускался перед всеми другими процессами, которые нуждаются в конфигурации.
О, я рад, что никто не предлагал параметризованные модули:
Ответ 4
Я бы сделал вариант 1 для статической конфигурации. Вы всегда можете проверить, установив параметры через application:set_env/3,4
. Причина, по которой вы хотите сделать это, заключается в том, что ваши тесты приложения должны будут запускать все приложение в любом случае в какое-то время. И возможность установки тестовой конфигурации в этот момент действительно опрятная.
Контроллер приложения работает по умолчанию, поэтому вам не нужно запускать приложение (вам тоже нужно это сделать!)
Наконец, если процесс нуждается в конкретной конфигурации, скажем так в данных конфигурации! Вы можете сохранить любой термин Erlang, в частности, вы можете сохранить термин, который позволяет вам переопределить параметры конфигурации для определенного node.
Для динамической конфигурации вам, вероятно, будет лучше, используя gen_server
или используя новейшие функции gproc
, которые позволяют хранить такую динамическую конфигурацию.
Ответ 5
Я также видел, что люди используют файл заголовка .hrl(файл заголовка erlang), где вся конфигурация определена и включает его в начале любого файла, который нуждается в конфигурации.
Он обеспечивает очень краткий поиск конфигурации, и вы получаете конфигурацию произвольной сложности.
Я считаю, что вы также можете перезагрузить конфигурацию во время выполнения, выполнив перезагрузку модуля. Недостатком является то, что если вы используете конфигурацию в нескольких модулях и перезагружаете только один из них, только один модуль обновит конфигурацию.
Тем не менее, я на самом деле не проверял, работает ли он так, и я не смог найти окончательную документацию о том, как перезагрузка перезагрузки .hrl и hot code перезагружается, поэтому убедитесь, что вы дважды проверяете это прежде чем вы его используете.