简体   繁体   中英

Concurrent requests prevention

I want to make some methods of my API to be locked ( HttpStatus.Conflict ) until another one with same params not finished (like ?id=1&key=sd6gd0f1g5ds16fh), like if bad user try to make 2+ same request at once, only one will be done. My idea was to use 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;
        }
    }
}

I'm using it like this:

[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));
}

But I'm pretty new to it and have some questions:

  1. Am I going in true direction?
  2. When I'm calling Dispose , Semaphore still exists on next request. What's wrong?
  3. Will my class be disposed (and Semaphore released) on some exception? (Like we can see above, if service == null )

This is not perfect and there is room for improvement but thought it might start you off an another direction or way to thinking.

Work your Semaphore stuff into locking the static dictionary

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM