并发请求预防

本文关键字:请求 并发 | 更新日期: 2023-09-27 18:17:08

我想让我的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;
}