c# - 并发请求预防

标签 c# asp.net-web-api2

我想让我的 API 的一些方法被锁定(HttpStatus.Conflict)直到另一个具有相同参数的方法没有完成(比如?id=1&key=sd6gd0f1g5ds16fh),就像坏用户尝试一次提出 2+ 个相同的请求,只会完成一个。 我的想法是使用 Semaphore:

public class Lock : IDisposable
{
    private bool _disposed = false;

    private readonly Semaphore _semaphore;

    public bool IsLocked
    {
        get;
        private set;
    }

    public Lock(string name)
    {
        this.IsLocked = false;
        try
        {
            this._semaphore = Semaphore.OpenExisting(name);
            this._semaphore.Close();
        }
        catch (Exception)
        {
            this._semaphore = new Semaphore(0, 1, name);
            this.IsLocked = true;
        }
    }

    ~Lock()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this._disposed)
        {
            if (disposing)
            {
                this._semaphore.Release();
                this._semaphore.Dispose();
            }

            this._disposed = true;
        }
    }
}

我是这样使用它的:

[ActionName("Ping")]
[HttpGet]
public IHttpActionResult Ping([FromUri]int? id = null, [FromUri]string key = null)
{
    if (id == null)
    {
        //ProcessException is some wrap for api error answer
        throw new ProcessException(HttpStatusCode.BadRequest, "Service ID is required");
    }

    if (key == null)
    {
        throw new ProcessException(HttpStatusCode.BadRequest, "Service Key is required");
    }

    Lock serviceLock = new Lock("service." + id + "." + key);
    if (!serviceLock.IsLocked)
    {
        throw new ProcessException(HttpStatusCode.Conflict, "Other Service operation already in progress");
    }

    var service = Service.Get((int)id, key);
    if (service == null) // Right hereino
    {
        throw new ProcessException(HttpStatusCode.Forbidden, "Service ID and/or Key is invalid");
    }

    Service.Touch((int)id);

    serviceLock.Dispose();

    //JResponse is some wrap for Dictionary<string, object>
    return Ok(new JResponse(true));
}

但我对它很陌生,有一些问题:

  1. 我的方向是否正确?
  2. 当我调用 Dispose 时,Semaphore 在下一个请求中仍然存在。怎么了?
  3. 是否会在某些异常情况下处理(并释放 Semaphore)我的类? (就像我们在上面看到的,如果 service == null)

最佳答案

这并不完美,还有改进的余地,但我认为它可能会让您从另一个方向或方式开始思考。

使用信号量来锁定静态字典

//ToDo: You would have to make this ThreadSafe
public static class Helper
{
    public static Dictionary<string,ClientDto> ClientDtos 
    = new Dictionary<string, ClientDto>();
}

public class ClientDto
{
    public int ClientKey { get; set; }
    public string Key { get; set; }
    public DateTime CreatedOn { get; set; }
}

in your Global.asax add.
protected void Application_EndRequest()
{
    Helper.ClientDtos.Remove(SeesionId);
}

//if this is called twice by the same client and the request is 
//not finished processing the first request the second one will go into    
//RequestBeingHandled and just return preventing the code from preforming 
//the same action until the first/current is complete.

public IHttpActionResult Ping([FromUri]int? id = null, [FromUri]string key = null)
{
    if(RequestBeingHandled(id, key))
    {
        //
        Return .....
    }
    else 
    {
        //if not add 
        ClientDto client = new ClientDto();
        client.ClientKey = id;
        client.Key = key;
        client.CreatedOn = DateTime.Now;
        Helper.ClientDtos.Add(SeesionId, client);
    }
    //call some code to do stuff...
}

private bool RequestBeingHandled(int id, string key)
{
    //ToDo: write this code.
    //check if its already in the dic
    return bool;
}

关于c# - 并发请求预防,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33057932/

相关文章:

c# - 如何在 View 状态中存储对象?

c# - 将 JSON 字符串加载到 HttpRequestMessage

asp.net-core - 使用 httpClient.postasync 进行 Web api 调用 .netcore

c# - 为什么我没有得到我点击/选择的真实当前字符?

c# - 用列表条目替换字符串出现

c# - 异步传递方法

c# - 在 C# 中创建只读列表

asp.net-web-api2 - 如何将 web.config 添加到 owin 自托管 web api?

c# - OData 路径模板不是有效的 OData 路径模板

ios - ASP.NET 网络 API 2 : Login with external provider via native mobile (iOS) app