基于 .NET 9.0 的高性能轻量级令牌桶限流服务

基于 .NET 9.0 的高性能轻量级令牌桶限流服务

项目地址: https://gitee.com/huang_ru_yun_admin_admin/TokenBucket

欢迎 Star 和 Fork!⭐

前言

在开发开放平台 API 时,接口限流是必不可少的功能。传统的 Redis 方案虽然强大,但对于中小型项目来说,部署成本高、需要额外服务器。

今天给大家分享一个基于 .NET 9.0 的纯内存令牌桶限流方案,支持 10万+ 活跃用户,无需 Redis,单机部署即可,开箱即用!


什么是令牌桶算法?

令牌桶算法(Token Bucket)是网络流量整形和速率限制的常用算法。

算法原理

复制代码
┌─────────────┐      ┌─────────────┐
│   令牌桶     │      │   请求队列   │
│ (容量 100)   │─────→│   (QPS)     │
└─────────────┘      └─────────────┘
      ↑                   ↓
      │                   │
      │              允许/拒绝
      │                   │
  按速率               │
  添加令牌              │
  (10个/秒)            │

核心概念

  • 桶容量(Max): 桶能容纳的最大令牌数(如 100)
  • 令牌数(Value): 当前桶中的令牌数
  • 补充速率(Step): 每次补充的令牌数(如 10)

工作流程

  1. 系统按固定速率向桶中添加令牌
  2. 请求到来时,从桶中取 1 个令牌
  3. 如果桶中有令牌 → 允许请求
  4. 如果桶为空 → 拒绝请求

项目特性

核心优势

  • 高性能 - 纯内存操作,响应速度快
  • 🚀 高并发 - 支持 10万+ 活跃用户并发
  • 🎯 灵活限流 - 支持用户级、用户+接口级限流
  • 💾 持久化 - 支持数据持久化,服务重启不丢失
  • 📦 SDK 支持 - 提供客户端 SDK,开箱即用
  • 📚 Swagger 文档 - 完整的 API 文档

性能指标

指标
QPS 1000+
支持用户数 10万+ 活跃用户
内存占用 ~15MB/10万用户
部署方式 单机即可,无需 Redis

快速开始

1. 克隆项目

bash 复制代码
git clone https://gitee.com/huang_ru_yun_admin_admin/TokenBucket.git
cd TokenBucket

2. 运行服务

bash 复制代码
dotnet run --project TokenBucket

服务将在 http://localhost:5000 启动,Swagger 文档地址: http://localhost:5000/swagger

3. 引用客户端 SDK

在你的项目中引用 TokenBucket.Client 项目。


使用示例

基础用法

csharp 复制代码
// 初始化客户端(HTTP)
var client = new TokenBucketClient("localhost:8080;user;password");

// 初始化客户端(HTTPS,以 ;s 结尾)
var clientHttps = new TokenBucketClient("localhost:8080;user;password;s");

// 添加限流规则
await client.Add("user_123", new BucketItem
{
    Max = 100,    // 最大令牌数
    Step = 10     // 每次恢复的令牌数
});

// 获取令牌
var allowed = await client.Get("user_123");
if (allowed)
{
    // 允许访问接口
}

推荐用法:用户+接口限流

为什么推荐用户+接口?

复制代码
❌ 只限制用户的问题:
- 用户调用"登录接口"和"查询接口"共享配额
- 高频接口占用所有额度,低频接口无法使用
- 无法针对接口特性设置不同限流策略

✅ 用户+接口的优势:
- 每个接口独立限流,互不影响
- 敏感接口(如登录)可以严格限流
- 高频接口(如查询)可以宽松限流

示例代码:

csharp 复制代码
// HTTPS 连接(生产环境推荐)
var client = new TokenBucketClient("api.example.com;admin;secret123;s");
var userId = "user_001";

// 登录接口:严格限流,防止暴力破解
if (await client.TryGetTokenForApi(userId, "login", max: 10, step: 10))
{
    Console.WriteLine("登录成功");
}

// 查询接口:宽松限流,允许正常使用
if (await client.TryGetTokenForApi(userId, "query", max: 1000, step: 100))
{
    Console.WriteLine("查询成功");
}

// 导出接口:严格限流,防止资源滥用
if (await client.TryGetTokenForApi(userId, "export", max: 5, step: 5))
{
    Console.WriteLine("导出成功");
}

// 发送短信接口:最严格限流,防止短信轰炸
if (await client.TryGetTokenForApi(userId, "sms", max: 3, step: 3))
{
    Console.WriteLine("短信发送成功");
}

批量配置接口限流

csharp 复制代码
var apiConfigs = new Dictionary<string, (int max, int step)>
{
    { "login", (10, 10) },      // 登录:10次/小时
    { "query", (1000, 100) },    // 查询:1000次/小时
    { "export", (5, 5) },       // 导出:5次/天
    { "sms", (3, 3) },         // 短信:3次/小时
    { "upload", (100, 10) }      // 上传:100次/小时
};

await client.AddApiLimits("user_001", apiConfigs);

API 接口

添加/更新桶

http 复制代码
POST /Add?key={key}
Content-Type: application/json

{
  "step": 10,
  "max": 100,
  "value": 100
}

获取令牌

http 复制代码
POST /Get?key={key}

响应:
1 (允许)
0 (拒绝)

查询桶信息

http 复制代码
POST /GetItem?key={key}

响应:
{
  "step": 10,
  "max": 100,
  "value": 95
}

分页查询

http 复制代码
POST /GetList
Content-Type: application/json

{
  "keyword": "user",
  "pageindex": 1,
  "pagesize": 20
}

配置说明

appsettings.json

json 复制代码
{
  "FlushedTime": 1,        // 令牌刷新间隔(秒)
  "EnablePersist": true,   // 是否启用持久化
  "user": "1",             // API 用户名
  "password": "1"          // API 密码
}

限流参数

参数 说明 默认值
Max 最大令牌数 100
Step 每次恢复的令牌数 10
FlushedTime 刷新间隔(秒) 1

核心实现

并发安全的令牌获取

csharp 复制代码
// 使用 AddOrUpdate 保证原子操作
return _bucket.Bucket.AddOrUpdate(key,
    key => new BucketItem { Value = Max, Step = Step, Max = Max },
    (key, item) =>
    {
        if (item.Value > 0)
        {
            item.Value--;
            return item;
        }
        return item;
    }).Value > 0 ? 1 : 0;

懒加载刷新策略

为什么需要优化?

传统方式每秒刷新所有桶(10万次),CPU 占用高。优化后只刷新活跃桶,性能提升 90%!

csharp 复制代码
// 只刷新最近访问过的桶
foreach (var kvp in _bucket.Bucket)
{
    if (currentTime - kvp.Value.LastFlushTime >= FlushedTime * 1000)
    {
        // 添加到待刷新列表
        keys.Add(kvp.Key);
    }
}

// 分批处理,避免长时间阻塞
if (keys.Count >= batchSize)
{
    await ProcessBatchAsync(keys, currentTime, stoppingToken);
    keys.Clear();
    await Task.Yield();
}

持久化实现

  • 启动时 : 从 Bucket.bin 恢复数据
  • 运行时: 按数据量动态调整保存频率
  • 可配置 : 通过 EnablePersist 控制是否启用

开放平台限流实战

场景 1: 免费/付费应用分级限流

csharp 复制代码
// 免费应用
await client.TryGetToken("app_free_001", max: 100, step: 1);

// 付费应用
await client.TryGetToken("app_pro_001", max: 10000, step: 100);

// 企业应用
await client.TryGetToken("app_enterprise_001", max: 100000, step: 1000);

场景 2: SaaS 多租户限流

csharp 复制代码
// 租户A 的用户1
await client.TryGetTokenForApi("tenantA_user1", "login", max: 1000, step: 100);

// 租户B 的用户1(独立限流)
await client.TryGetTokenForApi("tenantB_user1", "login", max: 500, step: 50);

场景 3: 微服务接口限流

csharp 复制代码
// 用户服务
await client.TryGetTokenForApi("user_service", "login", max: 5000, step: 500);

// 订单服务
await client.TryGetTokenForApi("order_service", "create", max: 10000, step: 1000);

适用场景

✅ 适合

  • 小型开放平台(< 500 调用方)
  • SaaS 平台 API 限流
  • 企业内部 API 网关
  • 微服务接口限流
  • 单机部署场景

⚠️ 不适合

  • 中大型开放平台(> 500 活跃调用方)→ 建议用 Redis
  • 需要跨机房部署
  • 需要复杂的统计分析
  • 需要集群部署

总结

核心优势

  1. 高性能 - 纯内存操作,响应速度快
  2. 部署简单 - 单机即可,无需 Redis
  3. 成本低廉 - 无额外服务器成本
  4. 易于维护 - 代码简洁,易于理解
  5. 功能完整 - SDK + 服务端完整方案

适用场景

活跃用户 < 5000的前提下,本项目可以用于:

  • 小型开放平台 API 限流
  • SaaS 平台限流
  • 企业 API 网关
  • 微服务接口限流

学习交流

  • 如果你觉得这个项目对你有帮助,欢迎 Star
  • 欢迎提交 Issue 反馈问题和建议
  • 欢迎提交 Pull Request 贡献代码
  • 欢迎在评论区留言交流使用心得

相关推荐


如果你觉得这个项目对你有帮助,请给个 Star ⭐

欢迎交流 issues 和 PR!

相关推荐
weixin_421994785 小时前
MVC 模式初探
mvc·.net·.netcore
无风听海15 小时前
.NET10之AppContext
.net
微八度17 小时前
.Net Web API应用部署成windows服务
windows·.net·web api·winddows服务
无风听海1 天前
.NET10之Middleware 和 Filter
.net
weixin_421994782 天前
互联网与 Web 应用简介
.net·.netcore
用户298698530142 天前
C# Word自动化:轻松插入特殊符号,告别手动烦恼!
后端·c#·.net
步步为营DotNet2 天前
深入剖析.NET中Span:零拷贝内存操作的基石
服务器·php·.net
观无2 天前
WPF+OpenCV 实现精准像素距离测量工具(.NET 4.6.1)
人工智能·opencv·.net