Разработка решений на платформе ELMA365 > Переносимые сервисы в модулях / Рекомендации по разработке микросервисов для переносимых сервисов

Рекомендации по разработке микросервисов для переносимых сервисов

Настройки сервиса

Инициализация и работа микросервиса может подразумевать наличие некоторых настроек. Бывают ситуации, когда потребители микросервиса должны уметь сконфигурировать его для себя. Например, задать строку подключения к какому-то источнику данных. В этом случае следует добавить возможность получения настроек. У разработчика Модуля имеется возможность передавать значения переменных окружения в поднимаемый контейнер. За счёт получения данных из них можно реализовать конфигурирование своего микросервиса.

В зависимости от выбранного для разработки фреймфворка это может быть либо прямое считывание значений из переменных окружения, либо конфигурирование через специальный провайдер, как например в .NET.

При таком подходе разработчик Модуля сможет подать свои данные для микросервиса или даже предоставить эту возможность конечному пользователю.

Следует учитывать, что переменные среды задаются при инициализации контейнера, и изменения, вносимые конечным потребителем модуля на лету, не будут применяться.

Предоставляемый API

Разработчики Модуля имеют возможность взаимодействовать с микросервисом путём отправки HTTP-запросов из скриптов, например, в процессах или виджетах. В случае необходимости доступа к этой возможности микросервиса нужно на его стороне проработать и реализовать веб-API.

Предоставляемые API следует задокументировать для удобства его использования разработчиком Модуля.

Организация общения между двумя переносимыми сервисами

При необходимости можно организовать общение двух микросервисов в рамках одного модуля через веб-API.

Предположим, нам надо с микросервиса #1 отправить команду на микросервис #2. Для этого в микросервисе #1 должно быть задано получение из переменной окружения адреса микросервиса #2:

serv2url, exists := os.LookupEnv("serv2Url")

Имея его адрес, мы можем отправить HTTP-запрос:

resp, err := http.Get(serv2url + "/hello")

Чтобы в переменной окружения serv2Url находился путь до микросервиса #2, разработчику Модуля нужно добавить в настройку Переносимого сервиса #1 переменную окружения serv2Url, указав в ней шаблон имени Переносимого сервиса #2. Например, {$_srv_serv2}.

Предоставляемую возможность необходимо задокументировать, чтобы разработчик Модуля смог ей воспользоваться.

Порты

По умолчанию переносимый сервис настроен на работу с портом 3000. Если порт микросервиса будет отличаться от этого значения, следует указать это в документации модуля.

Состояние микросервиса

На данный момент переносимые сервисы не поддерживают хранение состояния микросервисов. Это значит, что при остановке микросервиса данные, циркулируемые в нём, будут утеряны.

Поэтому не стоит использовать файловую систему для долгосрочного хранения данных микросервиса.

Пример:

Нельзя: принимать на постоянное хранение на диске файлы картинок, чтобы потом доставать их по запросу пользователей.

Можно: временно скидывать на диск файлы картинок, чтобы сразу же обработать и вернуть их пользователю.

Логирование

При разработке микросервиса учтите, что на данный момент у пользователей не будет возможности получать логи через файловую систему или консоль. Если требуется добавить логи, их нужно возвращать либо через специально разработанный под них API, либо отправлять в какую-либо внешнюю систему, куда у пользователя будет доступ.

Начало внимание

При разработке учитывайте, что микросервис не сохраняет свое состояние, поэтому логи, хранимые в файлах, будут утеряны при выключении сервиса.

Конец внимание

Пример на Go

Стандартным потоком вывода в Go является объект os.Stdout, который фактически представляет консоль. В самом простом случае мы можем вывести в этот поток данные, импортировав модуль log:

log.Print("Hello log")
log.Printf("Hello log - %s", "hi yourself")

Кроме того, можно использовать специализированные библиотеки логирования, обладающие широкими функциональными возможностями, включая уровни логирования.

Примером такой библиотеки может быть zap. Ниже приведён пример вывода логов, выводимых в формате, соответствующем .e365:

// Инициализируем.
{
enc := zap.NewProductionEncoderConfig()
enc.TimeKey = "timestamp"
enc.EncodeTime = zapcore.ISO8601TimeEncoder
 
opts := []zap.Option{
zap.AddCaller(),
}
 
logger := zap.New(zapcore.NewCore(
zapcore.NewJSONEncoder(enc),
zapcore.Lock(os.Stderr),
zapcore.InfoLevel, // Требуемый уровень логирования
), opts...)
logger = l.Named("MyService1")
 
zap.ReplaceGlobals(logger)
}
// Получаем логер.
{
l = zap.L()
}
// Выводим лог.
{
l.Info("Hello zap-log")
}

Пробы

При использовании микросервисов бывают ситуации, когда к ним предъявляются повышенные требования к работоспособности, надёжности и актуальности. Для таких случаев Kubernetes предоставляет специальную опцию, которую называют пробами. Разработчик микросервиса проектирует и реализует точки, при обращении к которым можно получить однозначно интерпретируюмую информацию о работоспособности микросервиса. При разворачивании микросервисов к ним будут обращаться пробы, чтобы запросить информацию о текущем состоянии микросервиса.

Переносимые сервисы поддерживают работу Readiness и Liveness проб. Они могут быть использованы по отдельности или одновременно для одного микросервиса. Их использование может обеспечить отсутствие траффика, пока микросервис не готов для этого, и требуется перезапустить его в случае поломки.

Рекомендуется дополнительно ознакомиться с Официальной документацией и рекомендациями по работе с пробами.

Следует максимально подробно и чётко описывать в документации способы обращения к пробам. Неверное описание или использование описания разработчиком Модуля приведёт к неработоспособности микросервиса или даже всего Модуля. Также крайне важно корректно реализовать точки входа проб на стороне микросервиса.

Если вы не предусмотрели наличие пробы, сообщите об этом в документации. Самостоятельный подбор проб разработчиком модуля крайне нежелателен.

Liveness

При работе микросервиса может возникнуть ситуация, когда он придёт в состояние, в котором будет не способен полноценно обеспечивать свою функциональность. И решением может послужить перезапуск сервиса. Для выполнения проверки на наличие такой ситуации существуют Liveness-пробы. Они выполняют некоторые действия, результатом которых будет определение статуса работоспособности.

При желании можно использовать HTTP-запрос, открытие TCP-соединения или команду в контейнере.

  • Для проведения HTTP-пробы Kubernetes отправляет HTTP GET-запрос микросервису. Если обработчик пути микросервиса, указанного в настройке пробы переносимого сервиса, возвращает код успеха, то микросервис рассматривается как живой. Если обработчик возвращает код ошибки, микросервис перезапускается. Любой код, больший или равный 200 и меньший 400, означает успех. Любой другой код интерпретируется как ошибка.
  • Для проведения TCP-пробы Kubernetes использует TCP-сокет. По конфигурации, указанной в настройке пробы переносимого сервиса, проводится попытка открыть сокет. Если удаётся установить соединение, контейнер будет считаться живым. Если нет, то микросервис перезапускается.
  • Для проведения пробы-команды в контейнере микросервиса исполняется команда, указанная в настройке пробы переносимого сервиса. Если команда выполнена успешна, она возвращает 0, и микросервис считается живым. Если команда возвращает ненулевое значение, то микросервис перезапускается.

Readiness

Есть ситуации, когда микросервис не готов обслуживать запросы. Например, пока проводится предварительная инициализация при старте или есть некоторая зависимость от других внешних микросервисов. В таких случаях нужно дождаться перехода в готовое состояние. Однако в это же время микросервис не должен обслуживать обращения к нему. Для обработки таких ситуаций можно использовать Readiness-пробы. Это позволяет микросервису сообщить, что он в данный момент не готов, и запрос будет отправлен на другой экземпляр микросервиса, если он имеется.

Настройки и поведения Readiness-проб аналогичны Liveness-пробам.

Документирование

При разработке микросервиса следует задокументировать:

  • предоставляемый API;
  • порт, через который следует обращаться к микросервису;
  • список переменных окружения, через которые можно конфигурировать микросервис;
  • наличие и атрибуты Liveness и Readliness.