如何为 .NET 在线客服系统的 Open Api 开放接口实现 QPS 限流功能

我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统。陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户。

而我收到的用户需求也越来越多,产品化的需求,个性化的需求都有。最近两天收到一个用户的开放接口需求,为客服系统的 Open Api 开放接口提供一个获取在线访客列表的接口。

如何为 .NET 在线客服系统的 Open Api 开放接口实现 QPS 限流功能

如何在 .NET 系统中实现 Open Api 我在前文分解过,今天我想分享的是如何为在线客服系统的开放接口设计实现一个 QPS 限流功能

如下图所示,在为用户提供接口的过程中,我对这个接口应用了基本的 QPS 限流技术。

如何为 .NET 在线客服系统的 Open Api 开放接口实现 QPS 限流功能


什么是 QPS 接口限流

QPS(Queries Per Second,查询每秒)接口限流是指对 API 或网络服务的访问进行限制,控制每秒钟可以接受的最大请求数量。这种限流机制常用于防止服务器因为过多的请求而被过载,从而保证服务的稳定性和性能。

QPS 限流的具体做法通常是:

  1. 设置最大 QPS 限制:对于每个用户、IP 地址或系统等,设定一个请求次数上限。比如,一个接口每秒钟最多允许 100 次请求。

  2. 超限处理:当请求数超过设定的 QPS 限制时,系统通常会拒绝多余的请求,返回错误信息(如 HTTP 429 Too Many Requests)。有时,也可能选择进行排队、重试等处理。

  3. 分级限流:为了避免单一用户或 IP 造成整个系统的拥塞,可以根据用户类型、接口的优先级等进行不同的限流策略。

  4. 突发流量处理:对于突发的高频请求,系统可能允许短时间内的超出限制的请求,但会有相应的窗口期或滑动窗口来平滑流量。

QPS 限流的目的通常是:

  • 防止恶意请求或过多请求导致服务崩溃。
  • 确保公平性,避免单个用户或请求占用过多资源。
  • 提高服务的可用性和稳定性。

.NET 接口有哪些QPS限流方案

1. 基于内存的限流(Memory-based Rate Limiting)

  • 这种方法使用内存中的数据结构(如 DictionaryQueue)来记录每个用户或请求的时间戳,从而计算每秒钟的请求数量。
  • 适用于流量较小、对性能要求较高的场景。

示例

public class MemoryRateLimiter {     private readonly int _maxQps;     private readonly Dictionary<string, Queue<DateTime>> _requests = new Dictionary<string, Queue<DateTime>>();      public MemoryRateLimiter(int maxQps)     {         _maxQps = maxQps;     }      public bool IsRequestAllowed(string key)     {         var now = DateTime.UtcNow;         if (!_requests.ContainsKey(key))         {             _requests[key] = new Queue<DateTime>();         }          var requestQueue = _requests[key];          // Remove requests older than 1 second         while (requestQueue.Count > 0 && (now - requestQueue.Peek()).TotalSeconds >= 1)         {             requestQueue.Dequeue();         }          // Check if the current request exceeds the limit         if (requestQueue.Count >= _maxQps)         {             return false;         }          requestQueue.Enqueue(now);         return true;     } } 

2. 基于令牌桶算法(Token Bucket)

令牌桶算法是一种流量控制算法,它通过给每个请求一个令牌来限制流量。每秒钟会向桶中添加一定数量的令牌,只有获取到令牌的请求才能通过。

  • 适合需要处理突发流量的场景,因为令牌桶可以在短时间内允许一定数量的超限请求。
  • .NET 没有内置令牌桶算法,但可以自己实现或使用一些开源库(例如 RateLimiter 包)。

示例

public class TokenBucketRateLimiter {     private readonly int _bucketCapacity;     private readonly int _tokensPerSecond;     private int _tokens;     private DateTime _lastRefillTime;      public TokenBucketRateLimiter(int bucketCapacity, int tokensPerSecond)     {         _bucketCapacity = bucketCapacity;         _tokensPerSecond = tokensPerSecond;         _tokens = bucketCapacity;         _lastRefillTime = DateTime.UtcNow;     }      public bool IsRequestAllowed()     {         var now = DateTime.UtcNow;         var elapsedSeconds = (now - _lastRefillTime).TotalSeconds;          // Refill tokens         if (elapsedSeconds > 1)         {             _tokens = Math.Min(_bucketCapacity, _tokens + (int)(elapsedSeconds * _tokensPerSecond));             _lastRefillTime = now;         }          // Check if we have tokens available         if (_tokens > 0)         {             _tokens--;             return true;         }          return false;     } } 

3. 基于滑动窗口(Sliding Window)

滑动窗口限流算法通过维护一个请求的时间窗口来限制请求次数。在窗口内,最多可以接受一定数量的请求。每次请求都会刷新这个窗口,并根据窗口内的请求数决定是否允许请求通过。

  • 适合对请求的时间分布进行精确控制的场景。
  • 实现相对复杂,但能更平滑地处理流量。

示例

public class SlidingWindowRateLimiter {     private readonly int _maxQps;     private readonly TimeSpan _windowSize;     private readonly Queue<DateTime> _requests = new Queue<DateTime>();      public SlidingWindowRateLimiter(int maxQps, TimeSpan windowSize)     {         _maxQps = maxQps;         _windowSize = windowSize;     }      public bool IsRequestAllowed()     {         var now = DateTime.UtcNow;          // Remove requests that are outside the window         while (_requests.Count > 0 && (now - _requests.Peek()).TotalMilliseconds > _windowSize.TotalMilliseconds)         {             _requests.Dequeue();         }          // Check if we can allow the request         if (_requests.Count < _maxQps)         {             _requests.Enqueue(now);             return true;         }          return false;     } } 

4. 使用第三方库(如 AspNetCoreRateLimit

AspNetCoreRateLimit 是一个非常流行的 .NET 库,可以帮助开发者轻松实现 API 的限流。它支持多种限流算法,如固定窗口、滑动窗口、令牌桶等,并且可以在 ASP.NET Core 中使用。

安装 NuGet 包

dotnet add package AspNetCoreRateLimit 

配置示例:

public void ConfigureServices(IServiceCollection services) {     // Add rate limiting services     services.AddInMemoryRateLimiting();     services.Configure<IpRateLimitOptions>(options =>     {         options.GeneralRules = new List<RateLimitRule>         {             new RateLimitRule             {                 Endpoint = "*",                 Period = "1s",   // Limit requests per second                 Limit = 100       // Max 100 requests per second             }         };     });      services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(); }  public void Configure(IApplicationBuilder app) {     app.UseIpRateLimiting(); } 

5. 使用 Redis 实现分布式限流

如果你的应用是分布式的,可以使用 Redis 来存储每个请求的时间戳或者计数器,从而实现跨多台服务器的 QPS 限流。

示例

public class RedisRateLimiter {     private readonly string _key;     private readonly int _maxQps;     private readonly IDatabase _database;      public RedisRateLimiter(string key, int maxQps, IConnectionMultiplexer redis)     {         _key = key;         _maxQps = maxQps;         _database = redis.GetDatabase();     }      public async Task<bool> IsRequestAllowedAsync()     {         var now = DateTime.UtcNow;         var timeFrame = TimeSpan.FromSeconds(1);          var requestCount = await _database.ListLengthAsync(_key);          // Remove expired requests from the list         await _database.ListRemoveAsync(_key, now.Add(-timeFrame).ToString());          if (requestCount < _maxQps)         {             // Add current request time to the list             await _database.ListLeftPushAsync(_key, now.ToString());             return true;         }          return false;     } } 

6. 基于 ASP.NET Core 中间件限流

在 ASP.NET Core 应用中,可以创建一个中间件来处理请求限流逻辑,并通过 HTTP 请求的头信息来告知客户端当前的请求状态。

示例

public class RateLimitingMiddleware {     private readonly RequestDelegate _next;     private readonly MemoryRateLimiter _rateLimiter;      public RateLimitingMiddleware(RequestDelegate next, MemoryRateLimiter rateLimiter)     {         _next = next;         _rateLimiter = rateLimiter;     }      public async Task InvokeAsync(HttpContext context)     {         if (!_rateLimiter.IsRequestAllowed(context.Connection.RemoteIpAddress.ToString()))         {             context.Response.StatusCode = 429;             await context.Response.WriteAsync("Rate limit exceeded.");             return;         }          await _next(context);     } } 

总结

在 .NET 中实现 QPS 限流有很多方案,对于小流量的应用,内存限流和基于中间件的限流方式可能已经足够,而对于高并发的分布式系统,可能需要 Redis 或者更复杂的算法,如令牌桶或滑动窗口。


简介下这个 .net 开发的小系统

https://kf.shengxunwei.com/

升讯威在线客服与营销系统是一款客服软件,但更重要的是一款营销利器。

  • 可以追踪正在访问网站或使用 APP 的所有访客,收集他们的浏览情况,使客服能够主动出击,施展话术,促进成单。
  • 可嵌入网站、手机 APP、公众号、或者通过 URL 地址直接联系客服。
  • 支持访客信息互通,可传输访客标识、名称和其它任意信息到客服系统,与您的业务系统对接。
  • 可全天候 7 × 24 小时挂机运行,网络中断,拔掉网线,手机飞行模式,不掉线不丢消息,欢迎实测。

如何为 .NET 在线客服系统的 Open Api 开放接口实现 QPS 限流功能

如何为 .NET 在线客服系统的 Open Api 开放接口实现 QPS 限流功能

希望能够打造: 开放、开源、共享。努力打造 .net 社区的一款优秀开源产品。

钟意的话请给个赞支持一下吧,谢谢~

发表评论

评论已关闭。

相关文章