» » Знакомимся с микросервисным фреймворком Moleculer

 

Знакомимся с микросервисным фреймворком Moleculer

Автор: admin от 11-02-2019, 11:30, посмотрело: 23

Привет, %habrauser%!



Сегодня я хочу рассказать тебе об одном отличном, на мой взгляд, микросервисном фреймворке Moleculer.



Знакомимся с микросервисным фреймворком Moleculer



Изначально этот фреймворк был написан на Node.js, но в последствии у него появились порты и на других языках таких как Java, Go, Python и .NET и, скорее всего, в ближайшем будущем, появятся и другие имплементации. Мы используем его в продакшене в нескольких продуктах уже около года и словами сложно описать, каким благословением он нам показался после использования Seneca и своих_велосипедов. Мы получили всё что нам нужно из коробки: сбор метрик, кэширование, балансировка, fault-tolerance, транспорты на выбор, валидация параметров, логирование, лаконичное объявление методов, несколько способов межсервисного взаимодействия, миксины и многое другое. А теперь по порядку.

Здесь можно посмотреть подробнее. Мы используем Redis транспорт, но планируем перейти на TCP с его выходом из экспериментального состояния.



На практике, при написании кода мы никак не взаимодействуем с этим компонентом. Просто нужно знать, что он есть. Используемый транспорт указывается в конфиге. Таким образом, для перехода с одного транспорта на другой, просто меняем конфиг. Всё. Примерно так:



// ./moleculer.config.js

module.exports = {
  transporter: 'redis://:pa$$w0rd@127.0.0.1:6379',
  // ... прочие параметры
}

Данные, по умолчанию, ходят в формате JSON. Но можно использовать что угодно: Avro, MsgPack, Notepack, ProtoBuf, Thrift, и т.д.

Service


Класс, от которого мы наследуемся при написании наших микросервисов.

Вот так выглядит простейший сервис без методов, который, тем не менее, будет обнаружен другими сервисами:

// ./services/telemetry/telemetry.service.js

const { Service } = require('moleculer');

module.exports = class TelemetryService extends Service {
  constructor(broker) {
    super(broker);
    this.parseServiceSchema({
      name: 'telemetry',
    });
  }
};

ServiceBroker


Утрируя, можно сказать, что это прослойка между транспортом и сервисом. Когда один сервис хочет как-то взаимодействовать с другим сервисом, он делает это через брокера (примеры будут ниже). Брокер занимается балансировкой нагрузки (поддерживает несколько стратегий, в том числе и кастомные, по умолчанию — round-robin), учетом живых сервисов, доступных методов в этих сервисах и т.д. Для этого ServiceBroker под капотом использует еще один компонент — Registry, но я не будут на нем останавливаться, для знакомства он нам не понадобится.

Наличие брокера даёт нам крайне удобную штуку. Сейчас попробую пояснить, но придется немного отойти в сторону. В контексте фреймворка есть такое понятие как node. Простым языком, нода — это процесс в операционной системе (т.е. то, что получается когда мы вводим в консоли «node index.js», например). Каждая нода это ServiceBroker с набором из одного или нескольких микросервисов. Да, вы не ослышались. Мы можем компоновать наш стек сервисов как нашей душей угодно. Чем это удобно? Для разработки мы стартуем одну ноду, в которой запускаются все микросервисы разом (по 1 штуке), всего один процесс в системе с возможностью очень легко подключить hotreload, например. В продакшене — отдельная нода под каждый экземпляр сервиса. Ну либо микс, когда часть сервисов в одной ноде, часть в другой, и тд (правда, я не знаю зачем так делать, просто для понимания, что и так можно сделать тоже).


При отсутствии переменной окружения подгружаются все сервисы из директории, иначе по маске. Кстати, broker.repl() — еще одна удобная фича фреймворка. При старте в режиме разработки мы тут же, в консоли, имеем интерфейс для вызова методов (то, что вы бы делали, например, через postman в своем микросервисе, который общается по http), только тут это намного удобнее: интерфейс в той же консольке, где выполнили npm start.

Межсервисное взаимодействие


Осуществляется тремя способами:

call


Наиболее часто используемый. Сделали запрос, получили ответ (или ошибку).

// Метод сервиса "report", который вызывает метод сервиса "csv".
async getCsvReport({ jobId }) {
  const rows = [];
  // ...
  return this.broker.call('csv.stringify', { rows });
}

emit


Используется, когда мы просто хотим оповестить другие сервисы, о каком-то событии, но нам не нужен результат.

// Метод сервиса "user" триггерит событие о регистрации.
async registerUser({ email, password }) {
  // ...
  this.broker.emit('user_registered', { email });

  return true;
}

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

Важным моментом является то, что событие получит только один экземпляр каждого типа сервиса, т.е. если у нас в живых 10 «mail» и 5 «subscription» сервисов которые подписаны на это событие, то по факту получат его только 2 экземпляра — один «mail» и один «subscription».

broadcast


То же самое, что и emit, только без ограничений. Все 10 «mail» и 5 «subscription» сервисов поймают это событие.

Валидация параметров


По умолчанию, для валидации параметров используется fastest-validator, вроде как очень быстрый. Но ничего не мешает использовать любой другой, например, тот же joi, если вам нужна более продвинутая валидация.

Когда мы пишем сервис, мы наследуемся от базового класса Service, объявляем в нем методы с бизнес-логикой, но эти методы являются «приватными», их не получится вызвать извне (из другого сервиса), пока мы явно этого не захотим, объявив их в специальной секции actions при инициализации сервиса (публичные методы сервисов в контексте фреймворка называются actions).


Миксины


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



Кэширование


Вызовы методов (экшнов) могут быть закешированы несколькими способами: LRU, Memory, Redis. Опционально можно задать по какому ключу будут кешироваться вызовы (по умолчанию используется object hash в качестве ключа кэширования) и с каким TTL.



Метод кеширования задается через конфиг ServiceBroker-а.



Логирование



Тут, впрочем, тоже все достаточно просто. Есть достаточно неплохой встроенный логгер который пишет в консоль, есть возможность задать кастомное форматирование. Ничего не мешает подрубить любой другой популярный логгер, будь то winston или bunyan. Подробный мануал есть в документации. Лично мы используем встроенный логгер, в проде просто подрубается кастомный форматтер на пару строчек кода который спамит в консоль JSON-ом, после чего средствами лог драйвера докера они попадают в graylog.



Метрики



При желании можно собирать метрики по каждому методу и трейсить это всё в каком-нибудь zipkin. Настраивается, так же как и кеширование, при объявлении метода (экшна).



Fault-tolerance



Фреймворк имеет встроенный circuit-breaker, который управляется через настройки ServiceBroker. Если какой-либо сервис сбоит и количество этих сбоев превышает определенный порог, то он будет помечен, как нездоровый, запросы к нему будут сильно ограничены, пока он не перестанет валить ошибками.



Бонусом, так же имеется настраиваемый индивидуально у каждого метода (экшна) fallback, в случае если мы допускаем, что метод может сбоить и, например, отдавать закешированные данные или заглушку.



Заключение



Внедрение этого фреймворка для меня стало глотком свежего воздуха, избавившего от огромного количества головняка (если не считать, что микросервисная архитектура это один большой головняк) и велосипедирования, сделало написание очередного микросервиса простым и прозрачным. В нем нет ничего лишнего, он простой и очень гибкий, а написать первый сервис можно уже через час-два после прочтения документации. Буду рад, если данный материал окажется вам полезным и в своём следующем проекте вы захотите попробовать это чудо, как это сделали мы (и пока ни разу об этом не пожалели). Всем добра!

Источник: Хабр / Интересные публикации

Категория: Программирование

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *