如何在WebApi操作中锁定长时间的异步调用? [英] How to lock a long async call in a WebApi action?

查看:487
本文介绍了如何在WebApi操作中锁定长时间的异步调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这种情况下,我有一个WebApi和一个端点,该端点在触发时会做很多工作(大约2-5分钟).这是一个具有副作用的POST端点,我想限制执行范围,以便如果有2个请求发送到该端点(应该不会发生,但比后悔更安全),则其中一个将不得不等待以避免竞争条件.

I have this scenario where I have a WebApi and an endpoint that when triggered does a lot of work (around 2-5min). It is a POST endpoint with side effects and I would like to limit the execution so that if 2 requests are sent to this endpoint (should not happen, but better safe than sorry), one of them will have to wait in order to avoid race conditions.

我首先尝试在控制器内部使用一个简单的静态锁,如下所示:

I first tried to use a simple static lock inside the controller like this:

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

这当然是不可能的,因为lock语句中的await.

this is of course not possible because of the await inside the lock statement.

我考虑的另一个解决方案是使用这样的SemaphoreSlim实现:

Another solution I considered was to use a SemaphoreSlim implementation like this:

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

但是,根据MSDN:

SemaphoreSlim类表示一种轻量级的,快速的信号量,当预计等待时间非常短时,可用于在单个进程中进行等待.

The SemaphoreSlim class represents a lightweight, fast semaphore that can be used for waiting within a single process when wait times are expected to be very short.

由于在这种情况下,等待时间甚至可能达到5分钟,因此我应该使用什么来进行并发控制?

Since in this scenario the wait times may even reach 5 minutes, what should I use for concurrency control?

编辑(响应 plog17 ):

我确实知道将任务传递给服务可能是最佳方法,但是,我不一定要在后台将请求完成后仍在运行的某些内容排队. 该请求涉及需要一些时间的其他请求和集成,但是我仍然希望用户等待该请求完成并获得响应. Cron作业预计每天仅在特定时间触发一次此请求.但是,还有一个选项可以由开发人员手动触发(大多数情况下是为了防止工作出错),并且我想确保如果开发人员(例如,意外地双重发送请求等.

I do understand that passing this task onto a service might be the optimal way, however, I do not necessarily want to queue something in the background that still runs after the request is done. The request involves other requests and integrations that take some time, but I would still like the user to wait for this request to finish and get a response regardless. This request is expected to be only fired once a day at a specific time by a cron job. However, there is also an option to fire it manually by a developer (mostly in case something goes wrong with the job) and I would like to ensure the API doesn't run into concurrency issues if the developer e.g. double-sends the request accidentally etc.

推荐答案

如果在给定时间只能处理一个此类请求,为什么不实施队列?

If only one request of that sort can be processed at a given time, why not implement a queue ?

采用这种设计,在处理长时间运行的请求时不再需要锁定或等待.

With such design, no more need to lock nor wait while processing the long running request.

流量可能是:

  1. 客户端POST/RessourcesToProcess,应尽快收到202-Accepted
  2. HttpController只需将任务排队即可继续执行(并返回202接受的任务)

  1. Client POST /RessourcesToProcess, should receive 202-Accepted quickly
  2. HttpController simply queue the task to proceed (and return the 202-accepted)

其他服务(Windows服务?)使下一个任务出队

Other service (windows service?) dequeue next task to proceed

在此过程中,客户端应该可以轻松获取以前发出的请求的状态:

During this process, client should be easily able to get status of requests previously made:

  • 如果未找到任务:404-未找到.找不到ID为123的资源
  • 如果任务处理:200-OK. 123正在处理.
  • 如果任务已完成:200-OK.过程响应.

您的控制器可能类似于:

Your controller could look like:

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中处理长时间运行的过程.确实,它可能不是最佳的设计选择.如果仍要这样做,则可能需要阅读:

This design suggests that you do not handle long running process inside your WebApi. Indeed, it may not be the best design choice. If you still want to do so, you may want to read:

  • Long running task in WebAPI
  • https://blogs.msdn.microsoft.com/webdev/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net/

这篇关于如何在WebApi操作中锁定长时间的异步调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆