Необходимо реализовать возможность приёма транзакций от разных клиентов для процессинга и дальнейшей отправки в разные банки. Клиенты могут присылать как равномерно (например, по 1шт в секунду), так и большими пачками (1000шт каждый час или 10000 раз в день), банки обрабатывают транзакции последовательно.
Для простоты интеграции необходимо использовать REST API для приёма транзакций
Необходимо исключить ситуации, когда:
Тысячи клиентов по несколько транзакции ожидают двух, у которых по 10000 транзакций
Несколько клиентов по 10000 ждут тысячи клиентов по несколько транзакций.
Ок, нужно реализовать сервис который обрабатывает транзакции
Первым делом, для клиентов у которых меньше 100 транзакций в запросе использовать отдельный инстанс сервиса, масштабировать горизонтально.
[Route("api/[controller]")] [ApiController] public class ProcessController : ControllerBase { [HttpPost("Queue")] public async Task<IActionResult> Queue([FromBody] RequestDTO request) { // Что-то вроде балансировки нагрузки var requestUri = request.TransactionsCount < 100 ? $"http://instance1:81/api/Process/Queue" : $"http://instance2:81/api/Process/Queue"; HttpClient client = new HttpClient(); var result = await client.PostAsJsonAsync(requestUri, request); var content = await result.Content.ReadAsStringAsync(); return Ok(content); } }
Да, но в случае, когда есть 3 клиента (Клиент 1: 10000 транзакций), (Клиент 2: 1000 транзакций), (Клиент 3: 100 транзакций) третий клиент будет ожидать пока выполнятся все 11000 транзакций первых двух клиентов
Может обрабатывать сперва запросы у которых меньше транзакций?
Тогда в случае, когда 1000 клиентов (Клиент 1: 10000 транзакций), (Клиент 2-500: 1000 транзакций), (Клиент 501 - 1000: 100 транзакций), Клиент 1 будет ожидать пока обработаются все мелкие запросы. Вариант увеличить мощность машины в котором инстанс для больших запросов, масштабировав вертикально тоже отметается.
Тогда придется добавить все запросы в очередь (или группировать по клиентам, или по другому параметру) и выполнять транзакции по очереди с каждого запроса. Это, по моему мнению, самый спр��ведливый вариант для клиентов, так как клиент не просто ждет пока дойдет его очередь, а постепенно получает callback от выполненных транзакций
if (Requests.Any()) { // С каждого запроса выполняем одну транзакцию List<Task> tasks = new List<Task>(); foreach(var request in Requests.ToList()) { // имитируем обработку транзакции tasks.Add(TransactionProcess()); request.TransactionsCount--; // имитируем callback logger.LogCritical($"[Client: {request.Client}] left {request.TransactionsCount} transactions"); if(request.TransactionsCount == 0) { // Если все транзакции клиента выполнены, удаляем запрос из очереди Requests.Remove(request); } } // Выполняем транзакции одного цикла параллельно await Task.WhenAll(tasks); }

А потом пакуем приложение с помощью docker compose. Результат тут
Тут мы можем увидеть, что для каждого клиента выполняется по одной транзакции:

А тут, то что "Load balancer" перенаправляет запросы с менее 100 транзакциями в первый инстанс:

Предлагайте как еще можно улучшить решение. Также предлагайте другие задачи на тему микросервисов
