并发请求预防

时间:2015-10-10 19:11:39

标签: 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

1 个答案:

答案 0 :(得分:-1)

这并不完美,还有改进的余地,但我认为这可能会让你偏离另一个方向或思考方式。

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

//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;
}