我有一个系统,用户可以在给定时间范围内执行3次请求。如何在C#中编程?我知道我可以给用户3属性(DateTime1,DateTime2,DateTime3),然后检查这些属性,但这感觉不对。有更聪明的方法吗?我也在考虑使用堆栈或队列,但不知道如何使用。 如果可以同时更改时间范围和请求限制(因此没有单独的属性),那就太好了。
示例: 窗是60分钟 用户1用尽了时间限制,并且被拒绝访问。 用户2尚未用完限制,因此被接受。
如上所述,时间表是滚动的。
答案 0 :(得分:2)
您可以定义自己的RollingTimeWindow
类,该类记录最近的成功请求,并且仅在时间允许的情况下才执行请求。
您可以使用此代码对其进行测试。按ENTER尝试执行请求,只有在时间窗口允许的情况下,它才会被执行。
static void Main(string[] args) {
//Only allow 3 requests in a time window of 10 seconds.
var r = new RollingTimeWindow(3, TimeSpan.FromSeconds(10), () => Console.WriteLine("Executed."));
while (true) {
Console.ReadLine(); //wait for press on Enter
r.PerformRequest();
}
}
这是一个简单的代码,可帮助您开始处理滚动时间窗口:
public class RollingTimeWindow {
private Queue<DateTime> _latestRequests = new Queue<DateTime>();
private readonly int _maxNumberOfRequests;
private readonly TimeSpan _windowSize;
private Action _action;
/// <summary>
/// Creates a new RollingTimeWindow performing requests if allowed by the time frame.
/// </summary>
/// <param name="maximumNumberOfRequests">Number of requests allowed in the window size</param>
/// <param name="windowSize">Rolling window size</param>
/// <param name="requestAction">Action to invoke when the request must be performed</param>
public RollingTimeWindow(int maximumNumberOfRequests, TimeSpan windowSize, Action requestAction = default) {
_action = requestAction;
_maxNumberOfRequests = maximumNumberOfRequests;
if (_maxNumberOfRequests < 0) {
throw new ArgumentException(nameof(maximumNumberOfRequests));
}
_windowSize = windowSize;
}
//Returns true if the request would be allowed, else false (NOT threadsafe)
public bool IsRequestAllowed() {
CleanQueue(DateTime.Now);
return _latestRequests.Count < _maxNumberOfRequests;
}
//Returns true if the request was performed, else false.
public bool PerformRequest() {
var now = DateTime.Now;
CleanQueue(now);
if (_latestRequests.Count < _maxNumberOfRequests) {
_latestRequests.Enqueue(now);
_action(); //perform the actual request
return true;
} else {
return false; //request not allowed
}
}
private void CleanQueue(DateTime now) {
//Cleans all requests older than the window size
while (_latestRequests.Count > 0 && _latestRequests.Peek() < now - _windowSize) {
_latestRequests.Dequeue();
}
}
}
请注意,这不是线程安全的,它也可以与Timer
对象一起实施,以自动使旧值出队。