Кто из нас не хочет создать большое приложение с правильной архитектурой? Все хотят. Иметь гибкость, возможность повторного использования и ясность логики.
Чтобы были домены, сервисы, их взаимодействие.
А иногда даже хочется, чтобы было почти как в Эрланге.
Идея создания фреймворка микросервисов для NodeJs уже не раз успешно реализовывалась — так по крайней мере у нас есть Сенека И Студия.
js , что, конечно, хорошо, но они определяют большие логические единицы.
С другой стороны, у нас есть обычные объекты, которые совместно используются в системе.
Внедрение зависимости или аналогичные методы, но они не обеспечивают достаточной ясности границ.
Иногда нужны «наноуслуги».
Термину «наноуслуга» дадим следующее определение: «независимые компоненты, которые взаимодействуют друг с другом в рамках одного процесса по контракту, без использования сети» .
По сути, это самые распространенные объекты, но различия все же есть — компонент «нано-сервис» четко описывает, какие именно функции других сервисов ему требуются, и однозначно перечисляет все экспортируемые функции.
Компонент не может попросить другой компонент сделать что-то вне контракта.
Разрешением всех зависимостей будет заниматься фреймворк, которому будет все равно, что было запрошено и в какой последовательности, единственное условие — граф зависимостей не должен быть циклическим и все необходимые сервисы в конечном итоге должны быть зарегистрированы до запуска системы.
"Останавливаться!" ты говоришь.
«Что не так с микросервисамиЭ» Микросервисы — отличное решение для разделения монолитного приложения и могут быть полезны для распределения задач по нескольким процессам.
Однако использование сети для связи снижает производительность, увеличивает накладные расходы, ограничивает стиль связи и может быть небезопасным.
Кроме того, ситуации недоступности услуги требуют наличия обработчиков у каждого потребителя.
Кроме того, сразу возникает вопрос, кто будет обслуживать инфраструктуру десятков сервисов (процессов), следить за их статусами, каков сценарий их перезапуска, удаления и т. д. Например, микросервисы не подходят, если вам нужно разделить обычное приложение на компоненты — разделение модуля логирования, доступа к базе данных и валидатора на отдельные микросервисы выглядит неправильным решением проблемы.
Микросервисы слишком велики и дороги для таких задач.
«Хорошо, тогда почему не классический DIЭ» Да, инверсия зависимостей позволяет переложить работу по созданию объектов на фреймворк, но никак не может ограничить их использование.
Кроме того, зачастую сгоряча вместо конкретного сервиса зависимым может стать сам контейнер, у которого можно динамически запрашивать что угодно.
Этот стиль жестко фиксирует компонент в системе, что делает его извлечение и повторное использование практически невозможным.
«А альтернатива — наноуслугиЭ» Да, нано-сервис вполне может стать подходящим инструментом.
Сначала я кратко опишу структуру Антинит , созданный для реализации этого подхода, а затем я предоставлю код. Схематически концепция выглядит так:
Доступные компоненты Фу И Бар , которые соответственно расположены в областях Услуги И Общий .
Методы экспорта компонентов доФу И GetBar , который может быть запрошен другими компонентами.
Домены, в свою очередь, регистрируются платформой и становятся доступными в рамках процесса, при этом все взаимодействия происходят через ядро.
Кроме того, фреймворк предоставляет метод доступа к компонентам «извне», позволяя центральной точке запуска приложения взаимодействовать с компонентами.
Также упомянем, что существует механизм разделения прав доступа к методам компонента; подробнее об этом позже.
Код для этого приложения:
// first service file aka 'foo_service' class FooService { getServiceConfig () { return ({ require: { BarService: ['getBar'] }, export: { execute: ['doFoo'] }, options: { injectRequire : true } }) } doFoo (where) { let bar = this.BarService.getBar() return `${where} ${bar} and foo` } } export default FooService
// first layer file aka 'services_layer'
import { Layer } from 'antinite'
import FooService from '.
/foo_service'
const LAYER_NAME = 'service'
const SERVICES = [
{
name: 'FooService',
service: new FooService(),
acl: 711
}
]
let layerObj = new Layer(LAYER_NAME)
layerObj.addServices(SERVICES)
// second service file aka 'bar_service'
class BarService {
getServiceConfig () {
return ({
export: {
read: ['getBar']
}
})
}
getBar () {
return 'its bar'
}
}
export default BarService
// second layer file aka 'shared_layer'
import { Layer } from 'antinite'
import BarService from '.
/bar_service'
const LAYER_NAME = 'shared'
const SERVICES = [
{
name: 'BarService',
service: new BarService(),
acl: 764
}
]
let layerObj = new Layer(LAYER_NAME)
layerObj.addServices(SERVICES)
// main start point aka 'index'
import { System } from 'antinite'
// load layers, in ANY orders
import '.
/services_layer'
import '.
/shared_layer'
let antiniteSys = new System('mainSystem')
antiniteSys.onReady()
.
then(function() {
let res = antiniteSys.execute('service', 'FooService', 'doFoo', 'here')
console.log(res) // -> `here its bar and foo`
})
Как видно из кода, компоненты представляют собой обычные объекты с несколькими дополнительными методами, домены используют экземпляры компонентов, а центральная точка импортирует слои и получает доступ к определенному компоненту в определенном домене через системный вызов.
Кроме того, вызов методов в компонентах зависимостей — это простой вызов метода объекта; если он синхронный, его можно вызвать синхронно; если он асинхронный, то в соответствии с реализацией метода фреймворк не накладывает никаких ограничений в этом отношении.
Репозиторий содержит дополнительные примеры услуги.
«А как насчет накладных расходовЭ» Основная работа фреймворка происходит в момент запуска приложения, когда все зависимости разрешены.
Время задержки будет зависеть от размера системы, но в целом оно незаметно.
Возможны дополнительные задержки при асинхронной инициализации компонентов; здесь задержка будет определяться скоростью выполнения задачи (подключение к базе данных, открытие порта и т.п.
).
Накладные расходы на запуск системы минимальны.
Выполнение метода на другом компоненте происходит как поиск функции-обертки по ключу в словаре, после чего непосредственно выполняется метод компонента из функции-обертки.
Теперь о механизмах прав доступа.
Во-первых, при экспорте методов компонент явно указывает категорию экспорта — «читать», «писать», «выполнять» , таким образом, мы можем разделить их по степени воздействия на систему.
Во-вторых, при регистрации компонента слой задает маску доступа к компоненту, например '174' — указывает, что для системных вызовов доступны только методы категории 'выполнять' , компоненты находящиеся в одном домене - полный набор прав «читать», «писать», «выполнять» и компоненты из других доменов — только методы категорий 'читать' .
Таким образом, метод записи, экспортированный компонентом в одном домене, не может быть вызван компонентом в другом домене.
Если вы по ошибке напишете такую схему как зависимость, фреймворк откажется ее разрешать.
В инфраструктуре есть помощник для устаревшего кода, который может упростить процесс переноса кода.
Кроме того, конструкция платформы может помочь упростить отладку системы.
Есть отладчик процесса разрешения всех зависимостей, с его помощью станет понятно, где разрешение зависимостей выдает ошибку.
Важной особенностью фреймворка также является то, что в любой момент можно включить аудит системы, получая подробную информацию о том, какие компоненты взаимодействуют друг с другом и какие параметры передаются.
И вдобавок ко всему система может предоставить текущий график зависимостей, который легко визуализировать.
Есть простой помощник визуализации, Антинитовый визуальный инструментарий .
Эта библиотека сделана как пример возможной визуализации, возможно, не самый удачный.
Вот как вкратце выглядит концепция наносервисов, реализация фреймворка и инструментария для него.
Если у вас есть вопросы, предложения, дополнения, критика и предложения, пожалуйста, отразите это в комментариях.
Кроме того, проект имеет чат-чат .
На данный момент мне очень нужна обратная связь для улучшения прототипа.
Антинит доступны на github и для установки через НПМ по лицензии Массачусетский технологический институт .
Проект имеет подробную документацию и набор тестов.
ПС.
В настоящее время активно разрабатывается рабочий проект с использованием этого фреймворка; К сожалению, я не могу разглашать подробности, но никаких проблем на данном этапе выявлено не было.
Архитектура позволила вынести оказавшуюся ресурсоемкой задачу в отдельный процесс, полностью переиспользовав часть общего кода и внедрив зависимый макет сервиса, скрывающий межпроцессное взаимодействие.
Теги: #node.js #архитектура приложения #Микросервисы #JavaScript #программирование #node.js #Microservices
-
Юмор На Хабрахабре → Юмор На Хабрахабре
19 Oct, 24 -
Метапрограммирование С Примерами Javascript
19 Oct, 24