﻿# Рекомендации по разработке микросервисов для переносимых сервисов

> [HTML Version](development-key-points.html)

## Настройки сервиса

Инициализация и работа микросервиса может подразумевать наличие некоторых настроек. Бывают ситуации, когда потребители микросервиса должны уметь сконфигурировать его для себя. Например, задать строку подключения к какому‑то источнику данных. В этом случае следует добавить возможность получения настроек. У разработчика модуля имеется возможность передавать значения переменных окружения в поднимаемый контейнер. За счёт получения данных из них можно реализовать конфигурирование своего микросервиса.

В зависимости от выбранного для разработки фреймфворка это может быть либо прямое считывание значений из переменных окружения, либо конфигурирование через специальный провайдер, как например в **.NET**.

При таком подходе разработчик модуля сможет подать свои данные для микросервиса или даже предоставить эту возможность конечному пользователю.

Следует учитывать, что переменные среды задаются при инициализации контейнера, и изменения, вносимые конечным потребителем модуля на лету, не будут применяться.

## Предоставляемый API

C микросервисом можно взаимодействовать путём отправки HTTP-запросов из скриптов, например, в процессах или виджетах. Чтобы эта возможность стала доступна, на стороне микросервиса нужно проработать и реализовать web API. Об оптимальных подходах и инструментах создания web API читайте в статьях: 

- [«Проектирование веб-API RESTFUL»](https://docs.microsoft.com/ru-ru/azure/architecture/best-practices/api-design);

- [«Разработка web API»](https://habr.com/ru/post/181988/).

Предоставляемый API следует задокументировать для удобства его использования при дальнейшей работе с модулем.

### Организация взаимодействия между двумя переносимыми сервисами

В поставках ELMA365 On‑Premises и SaaS Enterprise можно настроить взаимодействие между двумя микросервисами в рамках одного модуля, используя переменные окружения.

Для этого:

1. Добавьте для сервиса 1 переменную окружения, в которой будет храниться адрес сервиса 2. Для этого откройте модуль, перейдите к редактированию переносимого сервиса и создайте переменную в [настройках Docker‑контейнера](portable-services.md#main-tab).

Допустим, для переменной окружения с адресом сервиса 2 можно задать название \[OBJECT\]. Eсли уникальное имя сервиса 2 — \[OBJECT\], то в качестве значения переменной нужно записать \[OBJECT\].

2. После этого на стороне переносимого сервиса 1 с помощью переменной можно настроить получение URL‑адреса сервиса 2:

````
serv2url, exists := os.LookupEnv("serv2Url")

3. ````
Используя полученный адрес, можно отправить HTTP-запрос:

````
resp, err := http.Get(serv2url + "/hello")

````
Предоставляемую возможность следует задокументировать, чтобы в дальнейшем ей можно было воспользоваться при работе с модулем.

### Порты

По умолчанию переносимый сервис настроен на работу с портом 3000. Если порт микросервиса будет отличаться от этого значения, следует указать это в документации модуля.

### Состояние микросервиса

На данный момент переносимые сервисы не поддерживают хранение состояния микросервисов. Это значит, что при остановке микросервиса данные, циркулируемые в нём, будут утеряны.

Поэтому не следует использовать файловую систему для долгосрочного хранения данных микросервиса. 

**Пример:**

Нельзя: принимать на постоянное хранение на диске файлы картинок, чтобы потом доставать их по запросу пользователей.

Можно: временно скидывать на диск файлы картинок, чтобы сразу же обработать и вернуть их пользователю.

## Логирование

При разработке микросервиса учтите, что на данный момент у пользователей не будет возможности получать логи через файловую систему или консоль. Если требуется добавить логи, их нужно возвращать либо через специально разработанный под них API, либо отправлять в какую-либо внешнюю систему, куда у пользователя будет доступ.

````
Начало внимание

````
При разработке учитывайте, что микросервис не сохраняет свое состояние, поэтому логи, хранимые в файлах, будут утеряны при выключении сервиса.

````
Конец внимание

## ````
Пример на Go

Стандартным потоком вывода в Go является объект \[OBJECT\], который фактически представляет консоль. В самом простом случае мы можем вывести в этот поток данные, импортировав модуль **log**:

````
log.Print("Hello log")  
log.Printf("Hello log - %s", "hi yourself")

````
Кроме того, можно использовать специализированные библиотеки логирования, обладающие широкими функциональными возможностями, включая уровни логирования.

Примером такой библиотеки может быть [zap](https://github.com/uber-go/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 проб. Они могут быть использованы по отдельности или одновременно для одного микросервиса. Их использование может обеспечить отсутствие траффика, пока микросервис не готов для этого, и требуется перезапустить его в случае поломки.

Рекомендуется дополнительно ознакомиться с [Официальной документацией](https://kubernetes.io/ru/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) и [рекомендациями по работе с пробами](https://habr.com/ru/company/flant/blog/470958/).

Следует максимально подробно и чётко описывать в документации способы обращения к пробам. Неверное описание или использование описания разработчиком модуля приведёт к неработоспособности микросервиса или даже всего модуля. Также крайне важно корректно реализовать точки входа проб на стороне микросервиса.

Если вы не предусмотрели наличие пробы, сообщите об этом в документации. Самостоятельный подбор проб разработчиком модуля крайне нежелателен.

### Liveness

При работе микросервиса может возникнуть ситуация, когда он придёт в состояние, в котором будет неспособен полноценно обеспечивать свою функциональность. И решением может послужить перезапуск сервиса. Для выполнения проверки на наличие такой ситуации существуют Liveness-пробы. Они выполняют некоторые действия, результатом которых будет определение статуса работоспособности. 

При желании можно использовать HTTP-запрос, открытие TCP-соединения или команду в контейнере.

- для проведения HTTP-пробы Kubernetes отправляет HTTP GET-запрос микросервису. Если обработчик пути микросервиса, указанного в настройке пробы переносимого сервиса, возвращает код успеха, то микросервис рассматривается как живой. Если обработчик возвращает код ошибки, микросервис перезапускается. Любой код, больший или равный 200 и меньший 400, означает успех. Любой другой код интерпретируется как ошибка;

- для проведения TCP-пробы Kubernetes использует TCP-сокет. По конфигурации, указанной в настройке пробы переносимого сервиса, проводится попытка открыть сокет. Если удаётся установить соединение, контейнер будет считаться живым. Если нет, то микросервис перезапускается;

- для проведения пробы-команды в контейнере микросервиса исполняется команда, указанная в настройке пробы переносимого сервиса. Если команда выполнена успешна, она возвращает 0, и микросервис считается живым. Если команда возвращает ненулевое значение, то микросервис перезапускается.

### Readiness

Есть ситуации, когда микросервис не готов обслуживать запросы. Например, пока проводится предварительная инициализация при старте или есть некоторая зависимость от других внешних микросервисов. В таких случаях нужно дождаться перехода в готовое состояние. Однако в это же время микросервис не должен обслуживать обращения к нему. Для обработки таких ситуаций можно использовать Readiness-пробы. Это позволяет микросервису сообщить, что он в данный момент не готов, и запрос будет отправлен на другой экземпляр микросервиса, если он имеется.

Настройки и поведения Readiness-проб аналогичны Liveness-пробам.

## Документация для микросервиса

При разработке микросервиса следует задокументировать:

- предоставляемый API;

- порт, через который следует обращаться к микросервису;

- [список переменных окружения](portable-services.md#variables-tab), с помощью которых можно конфигурировать микросервис;

- наличие и атрибуты Liveness и Readiness;

- [уникальное имя переносимого сервиса](portable-services.md#add-portable-microservices) для настройки развёртывания микросервиса с помощью [ConfigMap](configmap.md).