51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

.NetCore 常见限流策略、原理及使用方式

在分布式系统和高并发应用中,限流(Rate Limiting)是一项关键技术,用于防止过多的请求淹没服务器,从而保证服务的稳定性与性能。本文介绍几种常见的限流算法,包括固定窗口限流、滑动窗口限流、令牌桶算法、漏桶算法。


固定窗口限流 {#%E5%9B%BA%E5%AE%9A%E7%AA%97%E5%8F%A3%E9%99%90%E6%B5%81}

原理 {#%E5%8E%9F%E7%90%86}

固定窗口限流在一个固定的时间窗口内(如1秒、1分钟),限制请求数量。例如,一个用户在1分钟内最多只能发送100个请求。如果请求数超过阈值,则拒绝请求。时间窗口结束后,计数器重置。

优点 {#%E4%BC%98%E7%82%B9}

  • 实现简单:使用简单的计数器即可实现。

  • 低资源占用:不需要大量额外的计算和存储资源。

缺点 {#%E7%BC%BA%E7%82%B9}

  • 临界问题:在时间窗口的边界可能出现大量突发请求。例如,一个用户在窗口结束前发出多个请求,并在新窗口开始时再次发出多个请求,导致短时间内服务器受到过多请求的压力。

代码示例 {#%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B}

public class RateLimiterDemo
{
    private readonly int _maxRequests;
    private readonly TimeSpan _windowTime;
    private int _requestCount;
    private DateTime _windowStart;
public RateLimiterDemo(int maxRequests, TimeSpan windowTime)
{
    _maxRequests = maxRequests;
    _windowTime = windowTime;
    _windowStart = DateTime.UtcNow;
    _requestCount = 0;
}

public bool IsRequestAllowed()
{
    var now = DateTime.UtcNow;

    if (now - _windowStart > _windowTime)
    {
        _windowStart = now;
        _requestCount = 0;
    }

    if (_requestCount < _maxRequests)
    {
        _requestCount++;
        return true;
    }

    return false;
}

}



滑动窗口限流 {#%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E9%99%90%E6%B5%81}

原理 {#%E5%8E%9F%E7%90%86-1}

滑动窗口限流通过将时间窗口细分为多个小窗口,并对每个小窗口的请求进行统计来平滑流量。它在每个时间点都会计算过去固定时间内的请求总数,避免固定窗口的临界问题。

优点 {#%E4%BC%98%E7%82%B9-1}

  • 解决临界问题:滑动窗口避免了固定窗口临界点的突发流量问题。

  • 限流更加平滑:更加精准地控制请求频率。

缺点 {#%E7%BC%BA%E7%82%B9-1}

  • 实现复杂:需要存储每个小窗口的请求计数,并动态更新窗口。

  • 较高的资源开销:需要更多的计算和存储空间。

代码示例 {#%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B-1}

public class RateLimiterDemo
{
    private readonly int _maxRequests;
    private readonly TimeSpan _windowTime;
    private readonly LinkedList<DateTime> _requestTimestamps = new LinkedList<DateTime>();
public RateLimiterDemo(int maxRequests, TimeSpan windowTime)
{
    _maxRequests = maxRequests;
    _windowTime = windowTime;
}

public bool IsRequestAllowed()
{
    var now = DateTime.UtcNow;
    var windowStart = now - _windowTime;

    while (_requestTimestamps.Count &amp;gt; 0 &amp;amp;&amp;amp; _requestTimestamps.First.Value &amp;lt; windowStart)
    {
        _requestTimestamps.RemoveFirst();
    }

    if (_requestTimestamps.Count &amp;lt; _maxRequests)
    {
        _requestTimestamps.AddLast(now);
        return true;
    }

    return false;
}

}



令牌桶算法 {#%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95}

原理 {#%E5%8E%9F%E7%90%86-2}

令牌桶算法通过生成和消耗"令牌"来控制请求速率。系统以固定速率生成令牌并放入桶中,请求到达时从桶中取走一个令牌,如果桶中没有令牌,则拒绝请求。未使用的令牌可以累积,因此允许一定的突发流量。

优点 {#%E4%BC%98%E7%82%B9-2}

  • 支持突发流量:未使用的令牌可以累积,从而允许短期的流量高峰。

  • 灵活性高:可以通过调整令牌生成速率和桶的大小来调整限流策略。

缺点 {#%E7%BC%BA%E7%82%B9-2}

  • 精确控制较弱:突发流量过大时仍可能导致系统压力增加。

代码示例 {#%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B-2}

public class RateLimiterDemo
{
    private readonly int _capacity;
    private readonly int _tokensPerSecond;
    private int _tokens;
    private DateTime _lastRefillTime;
public RateLimiterDemo(int capacity, int tokensPerSecond)
{
    _capacity = capacity;
    _tokensPerSecond = tokensPerSecond;
    _tokens = capacity;
    _lastRefillTime = DateTime.UtcNow;
}

public bool IsRequestAllowed()
{
    var now = DateTime.UtcNow;
    var timeElapsed = (now - _lastRefillTime).TotalSeconds;

    // Refill tokens
    var tokensToAdd = (int)(timeElapsed * _tokensPerSecond);
    _tokens = Math.Min(_capacity, _tokens + tokensToAdd);
    _lastRefillTime = now;

    if (_tokens &amp;gt; 0)
    {
        _tokens--;
        return true;
    }

    return false;
}

}



漏桶算法 {#%E6%BC%8F%E6%A1%B6%E7%AE%97%E6%B3%95}

原理 {#%E5%8E%9F%E7%90%86-3}

漏桶算法将请求流量控制为一个固定速率。它模拟了一个装水的桶,水(请求)可以以任意速率流入桶中,但只能以固定速率流出(处理请求)。如果桶满了,超出的请求将被丢弃。该算法强制将请求处理速率保持稳定。

优点 {#%E4%BC%98%E7%82%B9-3}

  • 严格控制流量:漏桶算法能严格限制请求的处理速率,流量平滑。

  • 适用于带宽控制:特别适用于需要严格限速的场景。

缺点 {#%E7%BC%BA%E7%82%B9-3}

  • 不支持突发流量:多余的请求直接丢弃,无法处理突发流量。

代码示例 {#%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B-3}

public class RateLimiterDemo
{
    private readonly int _capacity;
    private readonly int _dripRate; // Requests per second
    private int _currentLoad;
    private DateTime _lastLeakTime;
public RateLimiterDemo(int capacity, int dripRate)
{
    _capacity = capacity;
    _dripRate = dripRate;
    _currentLoad = 0;
    _lastLeakTime = DateTime.UtcNow;
}

public bool IsRequestAllowed()
{
    var now = DateTime.UtcNow;
    var timeElapsed = (now - _lastLeakTime).TotalSeconds;

    // Leak the bucket
    var leaks = (int)(timeElapsed * _dripRate);
    _currentLoad = Math.Max(0, _currentLoad - leaks);
    _lastLeakTime = now;

    if (_currentLoad &amp;lt; _capacity)
    {
        _currentLoad++;
        return true;
    }

    return false;
}

}



总结 {#%E6%80%BB%E7%BB%93}

| 算法 | 优点 | 缺点 | 适用场景 | |--------|--------------|------------------------|--------------------| | 固定窗口限流 | 实现简单,计算资源开销小 | 存在临界问题,无法很好地控制短期内的突发流量 | 适合简单的API限流场景 | | 滑动窗口限流 | 解决临界问题,限流平滑 | 实现复杂,存储和计算开销较大 | 高并发场景、需要精确控制的限流场景 | | 令牌桶算法 | 支持突发流量,灵活调整 | 短期突发请求过大时可能无法很好地控制流量 | 实时通信、支付系统、API限流等场景 | | 漏桶算法 | 流量控制严格,平稳处理 | 不支持突发流量,突发请求可能被丢弃 | 带宽控制、流量整形等严格限速的场景 |


赞(1)
未经允许不得转载:工具盒子 » .NetCore 常见限流策略、原理及使用方式