Minimal Api как затормозить поток запросов?
В c# я не сильно давно, можно сказать, только познаю его. Однако, пытаюсь кой-чего реализовать в коде и возник один вопрос. Использую Minimal Api. Это там, где app.MapGet() и вот это вот всё. ну вроде такого:
app.MapGet("/stubs/handler_api.php", async (HttpRequest request, ActionHandler actionHandler) =>
{
return await actionHandler.DoAction(request);
});
Внутри DoAction есть ветвление, в зависимости от разобранного запроса он может быть отправлен к местную базу данных на MariaDB (11.4), либо может быть переадресован на другой сервис через http post запрос. Перед тем как выполнить этот http запрос в другой сервис, я его предварительно "форматирую" согласно требований самого сервиса. Проблема возникает в тот момент, когда на наш сервис идёт большой наплыв запросов. скажем, 100 в секунду. Вроде не много, но для второго сервиса (он не наш) это много и они просят соблюдать рекомендованные паузы, которые они отправляют ответом. Я получаю эту паузу из их ответа, но, не очень понял, что делать с ней дальше. Попробовал сделать так:
private Logger logger;
private List<UserLimit> userLimitList;
private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
private readonly System.Timers.Timer cleanupTimer;
private readonly TimeSpan inactiveTimeout = TimeSpan.FromSeconds(1);
public LimitsHandler(Logger logger)
{
this.logger = logger;
userLimitList = new List<UserLimit>();
cleanupTimer = new System.Timers.Timer(1000); // Проверять каждую секунду
cleanupTimer.Elapsed += DecrementLimitTimer;
cleanupTimer.Start();
}
public async Task SetLimit(int countryId, string serviceId, int limitTime)
{
try
{
if (await semaphore.WaitAsync(2000))
{
var limit = userLimitList
.Where(ul => ul.CountryId == countryId && ul.ServiceId == serviceId).FirstOrDefault();
if (limit == null)
{
userLimitList.Add(new UserLimit()
{
CountryId = countryId,
ServiceId = serviceId,
LimitTime = limitTime + 1
});
}
else
{
limit.LimitTime = limitTime + 1; // Обновление счетчика
}
}
}
catch (Exception ex)
{
logger.Error($"[SetUserLimit] Failed to lock the resource for update. {ex.ToString()}");
}
finally
{
semaphore.Release();
}
}
public async Task<bool> GetLimit(int countryId, string serviceId)
{
bool isLockAcquired = false;
try
{
isLockAcquired = await semaphore.WaitAsync(2000);
if (isLockAcquired)
{
var limitExists = userLimitList
.Any(ul => ul.CountryId == countryId && ul.ServiceId == serviceId);
return limitExists;
}
}
catch (Exception ex)
{
logger.Error($"[GetLimit] Failed to lock the resource for update. {ex}");
return true; // true - значит лимит присутствует
}
finally
{
if (isLockAcquired) // Освобождаем семафор только если он был успешно захвачен
{
semaphore.Release();
}
}
logger.Error("[GetLimit] Could not acquire the semaphore in the allotted time.");
return true; // true - значит лимит присутствует
}
private void DecrementLimitTimer(object sender, ElapsedEventArgs e)
{
lock (userLimitList)
{
for (int i = userLimitList.Count - 1; i >= 0; i--)
{
var limit = userLimitList[i];
limit.LimitTime--;
// Проверяем, если LimitTime достиг 0
if (limit.LimitTime <= 0)
{
logger.Info($"[DecrementLimitTimer] Removing limit for CountryId: {limit.CountryId}, ServiceId: {limit.ServiceId}");
userLimitList.RemoveAt(i);
}
}
}
}
При входе в DoAction выполняется вызов GetLimits. При первом входе там пусто и запрос свободно пролетает на второй сервис. от туда я ловлю лимит и выставляю там же вызывая SetLimit(). При повторном обращении в рамках этого временного лимита всё ок, юзер ловит паузу. Но, когда она проходит, то снова вся пачка запросов летит на второй сервис. Т.е. иными словами, запросы от юзеров ходят пачками, а мне бы их как то разделить во времени, чтобы они в итоге на второй сервис заходили чуть чуть друг за другом. Попытка воткнуть внутри GetLimit() семафоры результата не дали, так же как и Lock() (с ним вообще тормоза начались).
Да, чуть не забыл. ActionHandler и класс содержащий DoAction в контейнере зарегистрированы как Scoped. Вот даже есть мысль, что DoAction должен быт ь как Singleton и на входе поставить семафора? не уверен.
Заранее спасибо за помощь!