滚动时间窗口

时间:2019-09-18 05:28:19

标签: c# stack queue

我有一个系统,用户可以在给定时间范围内执行3次请求。如何在C#中编程?我知道我可以给用户3属性(DateTime1,DateTime2,DateTime3),然后检查这些属性,但这感觉不对。有更聪明的方法吗?我也在考虑使用堆栈或队列,但不知道如何使用。 如果可以同时更改时间范围和请求限制(因此没有单独的属性),那就太好了。

示例: 窗是60分钟 Example 用户1用尽了时间限制,并且被拒绝访问。 用户2尚未用完限制,因此被接受。

如上所述,时间表是滚动的。

1 个答案:

答案 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对象一起实施,以自动使旧值出队。