C# 并发请求预防
我想让我的API的一些方法被锁定(C# 并发请求预防,c#,asp.net-web-api2,C#,Asp.net Web Api2,我想让我的API的一些方法被锁定(HttpStatus.Conflict),直到另一个具有相同参数的方法没有完成(比如?id=1&key=sd6gd0f1g5ds16fh),比如如果坏用户试图一次发出2+相同的请求,那么只会执行一个。 我的想法是使用信号灯: public class Lock : IDisposable { private bool _disposed = false; private readonly Semaphore _semaphore; p
HttpStatus.Conflict
),直到另一个具有相同参数的方法没有完成(比如?id=1&key=sd6gd0f1g5ds16fh),比如如果坏用户试图一次发出2+相同的请求,那么只会执行一个。
我的想法是使用信号灯
:
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));
}
[ActionName(“Ping”)]
[HttpGet]
公共IHttpActionResult Ping([FromUri]int?id=null,[FromUri]string key=null)
{
if(id==null)
{
//ProcessException是api错误答案的一些包装
抛出新的ProcessException(HttpStatusCode.BadRequest,“需要服务ID”);
}
if(key==null)
{
抛出新的ProcessException(HttpStatusCode.BadRequest,“需要服务密钥”);
}
锁服务锁=新锁(“服务“+id+”+钥匙);
如果(!serviceLock.IsLocked)
{
抛出新的ProcessException(HttpStatusCode.Conflict,“其他服务操作已在进行”);
}
var service=service.Get((int)id,key);
if(service==null)//就在这里ino
{
抛出新的ProcessException(HttpStatusCode.Forbidden,“服务ID和/或密钥无效”);
}
Service.Touch((int)id);
Dispose();
//JResponse是字典的一些包装
返回Ok(新JResponse(true));
}
但我对它很陌生,有一些问题:
Dispose
时,信号量在下一个请求中仍然存在。怎么了
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;
}
//ToDo:您必须使此线程安全
公共静态类助手
{
公共静态字典ClientDtos
=新字典();
}
公共类客户端
{
public int ClientKey{get;set;}
公共字符串密钥{get;set;}
public DateTime CreatedOn{get;set;}
}
在您的Global.asax中添加。
受保护的无效应用程序\u EndRequest()
{
Helper.ClientDtos.Remove(请参见SIONID);
}
//如果同一客户机调用了两次,并且请求
//未完成处理第一个请求,第二个请求将进入
//RequestBeingHandled并返回阻止代码预执行
//在第一次/当前操作完成之前,执行相同的操作。
公共IHttpActionResult Ping([FromUri]int?id=null,[FromUri]string key=null)
{
if(正在处理的请求(id、密钥))
{
//
返回。。。。。
}
其他的
{
//如果没有添加
ClientDto client=新ClientDto();
client.ClientKey=id;
client.Key=Key;
client.CreatedOn=DateTime.Now;
Helper.ClientDtos.Add(请参见客户机ID);
}
//调用一些代码来做一些事情。。。
}
私有bool请求被处理(int-id,字符串键)
{
//ToDo:编写此代码。
//检查其是否已在驾驶员信息中心中
返回布尔;
}
您是否已登录不希望并发请求的用户?是否要限制来自同一会话的并发请求?还是想在任何地方限制所有请求?@NathanCooper没有登录。对每个请求按键进行身份验证(/service/ping?id=1&key=1337)。我想通过相同的id和密钥限制此ping的并发执行。好的,这里有一个问题,因为GET操作没有副作用。您为什么关心?请求默认为单线程。。。。您试图做的是限制同一客户机可以发出的请求量。但是为什么呢?无论如何,您可以简单地拥有一个静态字典,它记录客户端在最近一分钟或任何时间段内发出了多少请求。您需要建立一种机制来清除失败的响应,即在出现异常/错误时字典中留下的脏数据。@NathanCooper,这是错误的示例代码。还有其他方法应该只向数据库写入一次内容。但对于concurent请求,它可以被写入2次以上,因为首先我们从数据库中选择,检查所选信息,然后更新。使用命名的信号量
不是更好吗,比如“service.id.key”
?@ShamilYakupov我不知道在请求之间如何全局访问锁类。。。。。请求=q.e.g。q1、q2、q3、q4所有不同的客户端都有不同的锁类。。。。现在,如果它们都是相同的客户端,那么同样的事情,它们都有不同的锁类实例。。。所以这对我来说没有意义。您当前的设计是1q=1thread=1lock类,您需要的是1q=1thread和一个在所有q之间存在的全局。希望这能澄清这一点。互斥不适用于整个应用程序吗?我正在检查它是否按名称存在,如果存在,请阻止此并发操作。@ShamilYakupov lol你让我用谷歌互斥,互斥是一种编程技术,可以存档你想要的东西。。。但你并没有以这样的方式实施它。。。这就是我想告诉你的。因此,当前您的代码将永远无法工作,因为没有全局类。阅读一下他们是如何归档互斥的,然后你就会看到。。。。你需要在字典中实现它,这是我已经说过的。奇怪的是,有人否决了评论“并发请求预防”,这是不正确的!!!很奇怪,好像他们没有阅读或不理解。