1

У меня такой сценарий, когда у меня есть WebApi и конечная точка, которая при срабатывании выполняет много работы (около 2-5 минут). Это конечная точка POST с побочными эффектами, и я хотел бы ограничить выполнение, чтобы, если на эту конечную точку отправлено 2 запроса (не должно быть, но лучше безопасно, чем извините), одному из них придется подождать, чтобы избежать гонки условия.Как заблокировать длинный асинхронный вызов в действии WebApi?

Я первый попытался использовать простой статический замок внутри контроллера, как это:

lock (_lockObj) 
{ 
    var results = await _service.LongRunningWithSideEffects(); 
    return Ok(results); 
} 

это, конечно, не представляется возможным из-за await внутри lock заявления.

Другим решением я считал было использовать SemaphoreSlim реализации, как это:

await semaphore.WaitAsync(); 
try 
{ 
    var results = await _service.LongRunningWithSideEffects(); 
    return Ok(results); 
} 
finally 
{ 
    semaphore.Release(); 
} 

Однако, согласно MSDN:

SemaphoreSlim класс представляет собой легкий, быстрый семафор, который может быть использован для ожидая в течение одного процесса, когда ожидается, что время ожидания будет очень коротким.

Поскольку в этом случае время ожидания может достигать 5 минут, что я должен использовать для контроля параллелизма?

EDIT (в ответ на plog17):

Я понимаю, что, пройдя эту задачу на службу может быть оптимальным способом, однако, я не обязательно хочу стоять в очереди что-то в фоновом режиме, что до сих пор работает после того, как запрос будет выполнен. Запрос включает в себя другие запросы и интеграцию, которые занимают некоторое время, но я все равно хотел бы, чтобы пользователь дождался завершения этого запроса и получил ответ. Этот запрос, как ожидается, будет запускаться только один раз в день в определенное время с помощью задания cron. Тем не менее, есть возможность запустить его вручную разработчиком (в основном, если что-то пойдет не так с заданием), и я хотел бы, чтобы API не запускал проблемы параллелизма, если разработчик, например, дважды посылает запрос случайно и т. д.

ответ

1

Если только один запрос такого рода может быть обработан в заданное время, почему бы не реализовать очередь?

С такой конструкцией больше не нужно блокировать и не ждать при обработке долгого запроса.

потока может быть:

  1. POST Client/RessourcesToProcess, должны получить 202-Принимается быстро
  2. HttpController просто очереди задачу, чтобы продолжить (и вернуть 202-принят)

  3. Другое услуга (услуга Windows?) Dequeue следующая задача перейти

  4. Выполните задание
  5. Обновить статус ресурса

В ходе этого процесса, клиент должен быть легко в состоянии получить статус запросов ранее сделанных:

  • Если задача не найдено: 404-NotFound. Ressource не найден для id 123
  • Если обработка задачи: 200-OK. 123 - обработка.
  • Если задание выполнено: 200-OK. Реакция процесса.

Ваш контроллер может выглядеть следующим образом:

public class TaskController 
{ 

    //constructor and private members 

    [HttpPost, Route("")] 
    public void QueueTask(RequestBody body) 
    { 
     messageQueue.Add(body); 
    } 

    [HttpGet, Route("taskId")] 
    public void QueueTask(string taskId) 
    { 
     YourThing thing = tasksRepository.Get(taskId); 

     if (thing == null) 
     { 
      return NotFound("thing does not exist"); 
     } 
     if (thing.IsProcessing) 
     { 
      return Ok("thing is processing"); 
     } 
     if (!thing.IsProcessing) 
     { 
      return Ok("thing is not processing yet"); 
     } 
     //here we assume thing had been processed 
     return Ok(thing.ResponseContent); 
    } 
} 

Такая конструкция предполагает, что вы не обрабатываете длительный процесс, запущенных в WebAPI. Действительно, это не лучший выбор дизайна. Если вы все еще хотите сделать это, вы можете прочитать:

+0

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