【问题标题】:Rolling time window滚动时间窗口
【发布时间】:2019-09-18 05:28:19
【问题描述】:

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

示例: 窗口是 60 分钟 用户 1 已用完时间限制,并被拒绝访问。 用户 2 没有用完限制,所以被接受。

如前所述,时间范围是滚动的。

【问题讨论】:

  • 我认为我们需要更多详细信息才能回答您的问题。另外,你试过什么?
  • 用户请求什么数据?
  • 添加了说明。试图访问什么数据是无关紧要的。

标签: c# stack queue


【解决方案1】:

您可以定义自己的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 对象来实现,以自动将旧值出列。

【讨论】:

  • 这是一个非常好的实现!我会调查它,看看它是否在我的系统中工作。如果是,我会将其标记为已接受的答案。
  • 对我的系统进行一些调整后,它可以完美运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-13
  • 2020-07-13
  • 2018-07-07
  • 2018-07-26
  • 2021-05-12
  • 2019-10-21
相关资源
最近更新 更多