- PVSM.RU - https://www.pvsm.ru -
От переводчика: Это седьмая статья из цикла о Node.js [1] от команды Mozilla Identity, которая занимается проектом Persona [2].
Есть две основные проблемы, которые создают необходимость в конфигурации приложений:
Эти проблемы можно решить, инициализируя некоторые переменные в зависимости от текущего окружения и используя переменные окружения для хранения конфиденциальных данных. Общепринятый в среде Node.js шаблон для реализации этого подхода состоит в создании модуля, который экспортирует конфигурацию:
var conf = {
// окружение приложения -
// "production", "development", или "test
env: process.env.NODE_ENV || "development",
// IP адрес
ip: process.env.IP_ADDRESS || "127.0.0.1",
// Порт
port: process.env.PORT || 0,
// Настройки БД
database: {
host: process.env.DB_HOST || "localhost:8091"
}
};
module.exports = conf;
Это работает неплохо, но есть ещё пара проблем:
node-convict [9] решает обе эти проблемы, предоставляя схему конфигурации, в которой можно задавать информацию о типах, значения по умолчанию, переменные окружения и документацию для каждой из настроек.
С использованием convict пример выше принимает такой вид:
var conf = convict({
env: {
doc: "The applicaton environment.",
format: ["production", "development", "test"],
default: "development",
env: "NODE_ENV"
},
ip: {
doc: "The IP address to bind.",
format: "ipaddress",
default: "127.0.0.1",
env: "IP_ADDRESS"
},
port: {
doc: "The port to bind.",
format: "port",
default: 0,
env: "PORT"
},
database: {
host: {
default: "localhost:8091",
env: "DB_HOST"
}
}
});
conf.validate();
module.exports = conf;
Здесь содержится практически та же самая информация, но представленная в виде схемы. Благодаря этому нам удобнее экспортировать её и отображать в удобочитаемом виде, делать валидацию. Декларативный формат делает приложение более надёжным и более дружественным ко всем членам команды.
Для каждого параметра настройки есть четыре свойства, каждое из которых помогает сделать приложение надёжнее и проще для понимания:
format
указывается или один из встроенных в convict типов (ipaddress
, port
, int
и т.д.) или функция для валидации пользовательских типов. Если во время валидации параметр не проходит проверку типа, возникает ошибка.env,
установлена, то её значение будет использовано вместо значения по умолчанию.doc
вполне очевидно. Преимущество включения документации в схему перед комментариями в коде состоит в том, что эту информация используется в методе conf.toSchemaString()
для более информативного вывода.
Над фундаментом из значений по умолчанию можно надстраивать дополнительные уровни конфигурации с помощью вызовов conf.load()
и conf.loadFile()
. Например, можно загружать дополнительные параметры из объекта JavaScript для конкретного окружения:
var conf = convict({
// схема та же, что и в предыдущем примере
});
if (conf.get('env') === 'production') {
// в боевом окружении используем другой порт и сервер БД
conf.load({
port: 8080,
database: {
host: "ec2-117-21-174-242.compute-1.amazonaws.com:8091"
}
});
}
conf.validate();
module.exports = conf;
Или же можно создать отдельные конфигурационные файлы для каждого из окружений, и загружать их с помощью conf.loadFile()
:
conf.loadFile('./config/' + conf.get('env') + '.json');
loadFile()
также может загружать несколько файлов сразу, если передать массив аргументов:
// CONFIG_FILES=/path/to/production.json,/path/to/secrets.json,/path/to/sitespecific.json
conf.loadFile(process.env.CONFIG_FILES.split(','));
Загружать дополнительные параметры через load()
и loadFile()
полезно, когда есть настройки для каждого из окружений, которые не стоит устанавливать в переменных окружения. Отдельные декларативные конфигурационные файлы в формате JSON позволяют нагляднее представить различия между параметрами в разных окружениях. А так как файлы загружаются с помощью cjson [10], они могут содержать комментарии, что делает их ещё более понятными.
Обратите внимание, что переменные окружения имеют наивысший приоритет, выше, чем настройки по умолчанию и настройки, загруженные через load()
и loadFile()
. Чтобы проверить, какие именно настройки действуют, можно вызвать conf.toString()
.
После того, как настройки загружены, можно запустить валидацию, чтобы проверить, все ли они имеют правильный формат в соответствии со схемой. В convict есть несколько встроенных форматов, таких как url
, ports
или ipaddress
, кроме того можно использовать встроенные конструкторы JavaScript (например Number
). Если свойство format
не задано, convict проверит тип параметра на совпадение с типом значения по умолчанию (вызвав Object.prototype.toString.call [11]). Приведённые ниже три схемы эквивалентны:
var conf1 = convict({
name: {
format: String
default: 'Brendan'
}
});
// если формат не указан, предполагаем, что тип должен быть
// такой же, как у значения по умолчанию
var conf2 = convict({
name: {
default: 'Brendan'
}
});
// более лаконичная версия
var conf3 = convict({
name: 'Brendan'
});
Формат можно задать и в виде перечисления, в явном виде задав перечень допустимых значений, например ["production", "development", "test"]
. Любое значение, которого нет в списке, не пройдёт валидацию.
Вместо встроенных типов, можно использовать собственные валидаторы. К примеру, мы хотим, чтобы параметр был строкой из 64 шестнадцатеричных цифр:
var check = require('validator').check;
var conf = convict({
key: {
doc: "API key",
format: function (val) {
check(val, 'should be a 64 character hex key').regex(/^[a-fA-F0-9]{64}$/);
},
default: '3cec609c9bc601c047af917a544645c50caf8cd606806b4e0a23312441014deb'
}
});
Вызов conf.validate()
возвратит детальную информацию о каждой ошибочной настройке, если такие есть. Это помогает избежать повторного развёртывания приложения при обнаружении каждой ошибки в конфигурации. Вот как будет выглядеть сообщение об ошибке, если мы попытаемся присвоить параметру key
из предыдущего примера значение 'foo'
:
conf.set('key', 'foo');
conf.validate();
// Error: key: should be a 64 character hex key: value was "foo"
node-convict расширяет стандартный шаблон конфигурирования приложений Node.js, делая его более надёжным и удобным для членов команды, которым не придётся разбираться в дебрях императивного кода, чтобы проверять или изменять настройки. Схема конфигурации даёт команде проекта больше контекста для каждой настройки и позволяет делать валидацю для раннего обнаружения ошибок в конфигурации.
Автор: ilya42
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/45488
Ссылки в тексте:
[1] цикла о Node.js: https://hacks.mozilla.org/category/a-node-js-holiday-season/
[2] Persona: http://ru.wikipedia.org/wiki/Mozilla_Persona
[3] Охотимся за утечками памяти в Node.js: http://habrahabr.ru/company/nordavind/blog/195494/
[4] Нагружаем Node под завязку: http://habrahabr.ru/company/nordavind/blog/195686/
[5] Храним сессии на клиенте, чтобы упростить масштабирование приложения: http://habrahabr.ru/company/nordavind/blog/196018/
[6] Производительность фронтэнда. Часть 1 — конкатенация, компрессия, кэширование: http://habrahabr.ru/company/nordavind/blog/196358/
[7] Пишем сервер, который не падает под нагрузкой: http://habrahabr.ru/company/nordavind/blog/196518/
[8] Производительность фронтэнда. Часть 2 — кешируем динамический контент с помощью etagify: http://habrahabr.ru/company/nordavind/blog/196818/
[9] node-convict: https://github.com/lloyd/node-convict
[10] cjson: https://github.com/kof/node-cjson
[11] Object.prototype.toString.call: http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
[12] Источник: http://habrahabr.ru/post/197166/
Нажмите здесь для печати.