Настройки приложения #
Документация #
- Cloud Native Configuration
- Документация на классы-окружения org.http4k.cloudnative.env
- Документация на класс Environment
- Документация на класс EnvironmentKey
Настройка приложения #
Приложение для своего функционирования необходимо иметь конфигурационные даннные для работы:
- местоположение базы данных;
- местоположение системы сохранения журналов работы приложения;
- соль для сохранения паролей;
- различные параметры для подсистем приложения и т.д.
Эти данные не могут быть указаны прямо в коде приложения, так как минимум пользователи приложения не должны устанавливать себе средства разработки для использования приложения. Пользователи предпочитают скачивать готовое приложение и его устанавливать.
Окружение для отладки, разработки, тестирования #
При рассмотрении вопроса о настройках следует учитывать, что приложение за время своей разработки запускается в различных условиях:
- В процессе разработки на локальном компьютере разработчика.
- В рамках запуска системных тестов на локальном компьютере разработчика или в системе непрерывной интеграции.
- При работе на тестовом окружении (предпродуктовая разработка).
- На множестве продуктовых установок на серверах клиентов.
Детали по настройке окружения и доступным ресурсам будут сильно отличаться в зависимости от условий запуска. Для части этих вариантов, например для тестирования, можно подготовить достаточно контролируемые условия для выполнения. Во всех остальных случаях приложению необходимо адаптироваться под конкретные условия.
Как раз для решения этих вопросов и необходимо обрабатывать настройки приложения.
Для веб-приложений настройки применяются при старте, что в значительной степени облегчает их обработку по сравнению с приложениями с графическим пользовательским интерфейсом. Настройки при таких условиях достаточно один раз считать, обработать и учитывать при обработке сетевых запросов.
Передача параметров приложению #
Настройки приложению могут быть переданы приложению различными способами:
- Записаны в конфигурационные файлы.
- Записаны в базу данных.
- Записаны в специальные хранилища настроек, например etcd или Windows-реестр.
- Переданы через переменные окружения.
Не все эти варианты самодостаточны, например для чтения информации из базы данных приложению сначала необходимо передать местоположение базы данных и информацию о том, где в этой базе данных находятся настройки.
В рамках современных течений (контейнеризация приложений, разработка веб-приложений как функций) приложения не имеют прямого доступа к файловой системе. Для хранения файлов и предоставления к ним доступа используются специальные службы-хранилища. При таких условиях изначально данные для настройки могут быть переданы исключительно через переменные окружения.
Также следует указать, что приложению доступны файлы из набора ресурсов. Однако их можно использовать скорее только для указания значений по умолчанию для настроек приложения.
Настройка переменных окружения в Gradle #
При запуске приложений с помощью системы сборки Gradle при использовании расширения application можно настроить список переменных окружения с помощью вызова метода JavaExec environment(String name, Object value)
. При написании Gradle-конфигурации на языке Groovy установка переменных окружения может выглядеть следующим образом:
run {
environment("DATABASE_JDBC", "jdbc:h2:tcp://localhost/database.h2")
standardInput = System.in
}
В примере устанавливается переменная окружения DATABASE_JDBC
со значением jdbc:h2:tcp://localhost/database.h2
.
Стоит также учитывать, что при запуске задачи наследуются значения переменных окружения, следовательно их можно настроить через параметры запуска задачи в рамках среды разработки или командного интерфейса.
Возможности библиотеки http4k #
Библиотека http4k предоставляет удобный инструментарий для получения настроек из различных источников с помощью механизма линз. Для использования данного механизма необходимо добавить в список зависимостей приложения необходимо добавить библиотеку http4k-cloudnative
в файл build.gradle
:
dependencies {
implementation group: "org.http4k", name: "http4k-cloudnative", version: http4kVersion
}
Затем можно воспользоваться разными источниками данных, описание которых можно подчерпнуть из API класса Environment.
- Переменные окружения,
Envinorment.ENV
. - Свойства виртуальной машины JVM,
Environment.JVM_PROPERTIES
. - Properties-файлы с жёсткого диска,
Environment.from()
. - Properties-файлы из ресурсов,
Environment.fromResource()
. - YAML-файлы с жёсткого диска,
Environment.fromYaml()
. - Зашитые настройки по умолчанию,
Environment.defaults()
.
При запуске приложение может получить информацию сразу из различных источников, это позволяет пользователю вносить правки удобным образом. С точки же зрения приложения необходимо сформировать:
- список поддерживаемых настроек;
- последовательность источников для получения информации.
Можно рекомендовать следующую последовательность получения данных: Properties-файл из JVM-ресурса, свойства JVM, переменные окружения, настройки по умолчанию.
Настройка линз для переменных окружения #
Для описания линз используется класс EnvironmentKey. Он позволяет сформировать линзы для получения данных из сформированного окружения. Порядок оформления линзы совпадает с линзами для запросов: необходимо выбрать базовый класс, затем указать тип данных для извлечения с помощью линзы, а затем указать терминатор.
Пример линзы для получения целого числа из свойства PORT
.
val portLens = EnvironmentKey.int().required("web.port")
Затем можно воспользоваться данной линзой на сформированном объекте-окружения.
Стоит обратить внимание, что настройки касаются различных подсистем приложения. Для разделения настроек для различных подсистем рекомендуется разделять эти настройки по префиксам: web.port
, db.host
, db.base
, db.user
, auth.provider
и т.д.
С точки же зрения кода данные линзы можно определить рядом с классами, предназначенными для извлечения информации по конкретному разделу.
data class AppConfig(
val webPort: Int,
) {
companion object {
val webPortLens = EnvironmentKey.int().required("web.port", "Application web port")
}
}
Настройка источников настроек #
Для большинства настроек приложения разумно предложить значения по умолчанию. А для других настроек приложения наоборот, стоит требовать указания их от пользователя. Примером первой настройки может служить порт приложения, а второй — соль для сохранения пароля.
Для формирования настроек по умолчанию используем Environment.defaults()
:
val portLens = EnvironmentKey.int().required("web.port")
val saltLens = EnvironmentKey.nonEmptyString().required("auth.salt")
val defaultEnv = Environment.defaults(
portLens of 9000,
)
Затем необходимо сформировать цепочку из объектов-окружения, определяющие последовательность по выборке данных.
val appEnv = Environment.fromResource("app.properties") overrides
Environment.JVM_PROPERTIES overrides
Environment.ENV overrides
defaultEnv
Важно: При передаче приложения на проверку обеспечьте, чтобы все необходимые параметры приложению были переданы и запуск с помощью системы сборки Gradle выполнялся корректно.
Задача #
Реализуйте настройку следующих параметров приложения для управления наборами треугольников:
- Порт веб-приложения.
- Параметры базы данных:
- Хост сервера базы данных.
- Порт сервера базы данных.
- Название базы данных.
- Имя пользователя.
- Пароль пользователя.
- Создайте класс для сохранения параметров веб-приложения,
WebConfig
. Класс можно разместить в пакетеconfig
корневого уровня. Класс должен сохранять целочисленный порт для хранилища. - Во вспомогательном объекте класса определите линзу для получения целочисленного значения из свойства
web.port
. - Также во вспомогательном объекте определите функцию для создания объекта
WebConfig
путём получения параметров из объекта типаEnvironment
. - Также во вспомогательном объекте определите окружение по умолчанию, где для свойства
web.port
определите значение1515
. - В пакете
config
создайте файлAppConfig.kt
. - В созданном файле создайте цепочку для получения информации из окружения, состоящую из следующих элементов:
- Свойства из ресурсов
/ru/ac/uniyar/config/app.properties
. - Свойства JVM-машины.
- Свойства переменных окружения.
- Свойства по умолчанию для класса
WebConfig
.
- Свойства из ресурсов
- Создайте пустой файл в ресурсах по пути
/ru/ac/uniyar/config/app.properties
. - Создайте функцию
readConfiguration
, которая будет возвращать объектWebConfig
с использованием созданной цепочки получения данных из окружения. - В функции
main
вашего приложения вызовите созданную функциюreadConfiguration
. - В функцию запуска веб-приложения помимо объекта доступа к базе данных передайте ещё и объект веб-конфигурации. Внутри данной функции используйте полученный номер порта для запуска приложения.
- Запустите приложение. Если всё сделано корректно, тогда веб-приложение будет запущено по порту
1515
. - В конфигурационном файле
app.properties
добавьте значение для свойстваweb.port
равным1616
. Для этого добавьте строку с содержимым:Перезапустите приложение. По какому порту было запущено приложение?web.port=1616
- Откройте файл настроек сборки проекта
build.gradle
. Добавьте в конфигурацию запуска установку переменной окруженияweb.port
равной1717
. Перезапустите приложение. На каком порту было запущено приложение? Что нужно сделать, чтобы приложение было запущено на порту1717
?
ВНИМАНИЕ приложение при сдаче должно работать по порту по умолчанию 9000, запрещено предоставлять приложения, работающие по другому порту.
Доработайте приложение таким образом, чтобы пользователь мог указать все параметры для доступа к базе данных. Некоторые комментарии по реализации:
- Создайте класс
DatabaseConfig
, в котором разместите:- параметры подключения к базе данных
- вспомогательный объект с линзами и методами по созданию объектов
DatabaseConfig
из переменных окружения - функцию по преобразованию параметров подключения к строке JDBC, шаблон:
jdbc:h2:tcp://$host:$port/$database
- Создайте класс
AppConfig
, в котором разместите ссылки на объектыWebConfig
иDatabaseConfig
. - Переработайте метод
readConfiguration
так, чтобы он формировал объектAppConfig
и учитывал значение окружения по умолчанию для классаAppConfig
. - В методе
main
передайте ссылку на объектDatabaseConfig
библиотеке Flyway (методperformMigrations()
), а также методу по инициализации подключения к базе данных (connectToDatabase()
). - Стоит ли использовать эти параметры в классе управления службами СУБД
H2DatabaseManager
?