Алгоритмы для выбора эндпоинта в распределённой системе:
- Случайный выбор: Простой и эффективный способ, но может привести к неэффективному использованию ресурсов, особенно если один из инстансов перегружен.
- Round-robin: Равномерное распределение запросов между инстансами, что помогает балансировать нагрузку. Однако, не учитывает текущую загрузку инстансов.
- Выбор наименее загруженного сервиса: Этот способ позволяет выбирать инстанс с наименьшей нагрузкой, что может повысить производительность и эффективность использования ресурсов (т.е. оценивает общую нагрузку на сервер, включая процессорное время, загрузка процессора, использование памяти, количество активных запросов, скорость сети, количество обработанных запросов в единицу времени и другие параметры).
- Least Connections: Выбор сервиса с наименьшим количеством активных соединений.
- Weighted Round-robin: Распределение запросов с учетом весов инстансов (например, более мощные серверы получают больший вес).
- Least Response Time: Выбор сервиса с наименьшим временем ответа.
Существует коллекция, в которой хранятся объекты типа EndpointCollectionParameter
(т.е. информация об активных эндпоинтах). Но бывает такое, что эндпоинт временно становится недоступен, соответственно, такой эндпоинт нужно будет удалить из коллекции. Однако этот объект так и останется в куче до тех пор, пока его не удалит сборщик мусора. Проблема в том, что когда восстановится соединение с эндпоинтом, понадобится создавать точно такой же эндпоинт в памяти (таким образом, эндпоинтов в управляемой куче будет два, что является не очень правильным с точки зрения использования памяти).
Для решения данной проблемы предполагается использование класса EndpointPool
, который будет управлять аллокацией/деаллокацией эндпоинтов в памяти, реализуя паттерн объектного пула.
Также есть диаграмма, которая более подробно демонстрирует принцип, на котором основана динамичность платформы в контексте выбора типа клиентского приложения (MVC, Blazor, WPF etc) и протокола передачи данных между бэкэнд-сервисами (WebAPI, gRPC).
На схемах отображены отдельные БД для разных сервисов, на на самом деле база данных может как общая для всех сервисов, так и раздельная (тем не менее, за счет репликации все сервисы должны работать с данными так, будто это одна и та же БД, т.е. иметь одинаковые таблицы и записи в них).
Для достижения гибкости платформы, контроллеры должны быть пустыми: желательно, чтобы вся логика обработки данных находилась в модулях BL. Таким образом, клиентское приложение может быть совершенно разным (ASP.NET MVC, Blazor, WPF, React.js etc), т.е. оно становится ответственным только за отображение данных на UI. Для клиентских приложений допускается коммуникация с БД только для работы с кэшем.
Клиентское приложение общается со своим бэкэнд-сервисом исключительно напрямую и в большинстве случаев по протоколу HTTP.
Микросервисы могут общаться между собой либо напрямую, либо через шину данных. Шина данных реализуется двумя возможными способами:
- общий вэб сервис для бэкенда и системных сервисов (своего рода прокси);
- использование брокеров сообщений (например, RabbitMQ).
Детали коммуникации между микросервисами:
- если сервис А знает, к какому сервису нужно обращаться:
- сервис А обращается к ресолверу, чтобы отправить запрос к сервису Б;
- ресолвер обращается к БД, чтобы определить способ взаимодействия, на основании этого вызывает соответствующий класс из библиотеки workflow-lib для коммуникации (напрямую по HTTP, напрямую по gRPC, прокси по HTTP, прокси по gRPC, RabbitMQ etc);
- в случае с любой коммуникацией по HTTP или gRPC вызывающий модуль просто дожидается ответ и отдаёт его тому модулю, который инициировал коммуникацию и вызвал ресолвер;
- в случае коммуникации по брокеру сообщений, мы просто отправляем статус, записано ли сообщение в очередь.
- если сервис А не знает, к какому сервису нужно обращаться:
- сервис А обращается к ресолверу, чтобы отправить запрос на следующий модуль;
- ресолвер обращается к БД, чтобы определить следующий модуль и способ взаимодействия с ним;
- после этого всё взамодействие между сервисами производится аналогично, как в предыдущем варианте.
Вышеперечисленные способы межсервисной коммуникции позволили бы очень гибко настраивать коммуникацию между микросервисами через конфиги или БД. Соответствующие классы для настройки межсервисной коммуникции можно найти в пространстве имён WorkflowLib.Shared.Models.Network.MicroserviceConfigurations.