You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 7 TokenBucketLimiter:如何获取实例调用TryReplenish补充令牌?

问题描述

我正在使用.NET 7新增的Rate Limiting功能,希望通过TokenBucketLimiter限制用户对API的访问:每个用户初始拥有10次调用权限,且支持用户“付费”获取额外调用次数。据了解可通过ReplenishingRateLimiter的TryReplenish()方法实现,但不清楚如何获取已在Startup中注册的限流器实例。

Startup中的注册代码如下:

services.AddRateLimiter(options =>
{
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(resource =>
    {
        var request = resource.Request.Headers["token"].ToString();
        return RateLimitPartition.GetTokenBucketLimiter(request, _ => new TokenBucketRateLimiterOptions
        {
            TokenLimit = 10,
            ReplenishmentPeriod = TimeSpan.Zero,
            TokensPerPeriod = 10,
            QueueLimit = 0,
            AutoReplenishment = false,
        });
    });

    options.RejectionStatusCode = 429;
});

希望实现的测试端点代码:

[HttpGet("replenish")]
public async Task<IActionResult> DummyEndpoint()
{
    var token = Request.Headers["token"].ToString();
    var limiter = //somehow get my limiter based on the token used as partition key;
    limiter.TryReplenish();
    return Ok();
}

请问这种方式是否可行,或是我理解有误,需要采用其他实现方式?


解决方案

你的思路方向是对的,但直接获取分区限流器实例的方式需要调整——PartitionedRateLimiter内部的分区实例不会直接暴露给外部获取。以下是可行的实现方案:

1. 自定义限流器存储

放弃直接从全局分区限流器中获取实例,改为自己维护每个用户(token)对应的TokenBucketLimiter实例:

  • 先注册一个单例字典用于存储限流器:
services.AddSingleton<ConcurrentDictionary<string, TokenBucketLimiter>>();
  • 修改Startup中的限流器注册逻辑,从自定义字典中获取或创建实例:
services.AddRateLimiter(options =>
{
    var limiterStore = services.BuildServiceProvider().GetRequiredService<ConcurrentDictionary<string, TokenBucketLimiter>>();
    
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(resource =>
    {
        var token = resource.Request.Headers["token"].ToString();
        var limiter = limiterStore.GetOrAdd(token, key => new TokenBucketLimiter(new TokenBucketRateLimiterOptions
        {
            TokenLimit = 10,
            ReplenishmentPeriod = TimeSpan.Zero,
            TokensPerPeriod = 10,
            QueueLimit = 0,
            AutoReplenishment = false,
        }));
        return RateLimitPartition.GetTokenBucketLimiter(_ => limiter);
    });

    options.RejectionStatusCode = 429;
});
  • 在端点中直接从字典获取实例并调用TryReplenish:
private readonly ConcurrentDictionary<string, TokenBucketLimiter> _limiterStore;

public YourController(ConcurrentDictionary<string, TokenBucketLimiter> limiterStore)
{
    _limiterStore = limiterStore;
}

[HttpGet("replenish")]
public IActionResult DummyEndpoint()
{
    var token = Request.Headers["token"].ToString();
    if (_limiterStore.TryGetValue(token, out var limiter))
    {
        // 可指定补充的令牌数量,默认TryReplenish()会补充TokensPerPeriod配置的数量
        limiter.TryReplenish(10); // 示例:补充10次调用权限
    }
    return Ok();
}

2. 关键注意事项

  • 要处理token为空的场景,避免空键引发异常;
  • 如果是分布式部署,内存字典无法跨实例共享,需改用Redis等分布式缓存存储用户令牌剩余量,此时可能需要自定义限流器逻辑,而非直接使用TokenBucketLimiter;
  • TryReplenish()默认补充TokensPerPeriod配置的数量,也可传入指定参数灵活控制补充的调用次数。

3. 原思路说明

原思路中直接获取分区内限流器实例不可行,因为PartitionedRateLimiter的内部分区是封装状态,没有提供公开的获取方法。通过自定义存储的方式,既能满足全局限流需求,又能灵活操作指定用户的限流器实例。


内容的提问来源于stack exchange,提问作者Paweł Wąsowski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 07:01:14