简体   繁体   English

并发请求预防

[英]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. 我想使我的API的某些方法被锁定( HttpStatus.Conflict ),直到另一个具有相同参数的方法没有完成(例如?id = 1&key = sd6gd0f1g5ds16fh),就像坏用户试图一次发出2个以上相同的请求一样,一个会完成。 My idea was to use Semaphore : 我的想法是使用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. 当我调用Dispose ,下一个请求仍会存在Semaphore What's wrong? 怎么了?
  3. Will my class be disposed (and Semaphore released) on some exception? 我的课程会在某些例外情况下被处置(并释放Semaphore )吗? (Like we can see above, if service == null ) (就像上面我们看到的,如果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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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