2.23接口调用配额限制

在与第三方 API(如微信、支付网关等)集成时,通常需要遵守其每日/每月调用次数限制。为避免因超限导致业务中断或封禁,框架提供了灵活的接口调用配额限制功能,支持按天、按周、按月、永久总次数等策略,并允许自定义策略。

在与第三方 API(如微信、支付网关等)集成时,通常需要遵守其每日/每月调用次数限制。为避免因超限导致业务中断或封禁,框架提供了灵活的接口调用配额限制功能,支持按天、按周、按月、永久总次数等策略,并允许自定义策略。

启用配额限制后,每次请求会根据配置的策略检查当前计数,若达到上限将直接中断请求并抛出 InvalidOperationException 异常(不会实际发送 HTTP 请求)。通过 HttpRequestBuilder.SetQuotaKey(key) 为每个请求指定配额键,并与 HttpClientOptions 中的配额配置关联。

配置方式#

1. 注册默认配额策略#

Startup.csProgram.cs 文件中注册默认配额策略:

cs
services.AddHttpRemote(builder =>{    builder.AddDefaultQuotaStrategies();   // 注册 daily、weekly、monthly、lifetime 四种策略});

2. 为指定 HttpClient 客户端配置配额限制#

在注册 HttpClient 时,通过 ConfigureOptions 设置 QuotaLimits 字典,将配额键与对应的限制策略关联:

cs
// 配置默认客户端services.AddHttpClient(string.Empty)    .ConfigureOptions(options =>    // 或使用重载:.ConfigureOptions((options, serviceProvider) =>    {        options.QuotaLimits = new Dictionary<string, HttpQuotaLimit>        {            ["wechat/accesstoken"] = new HttpQuotaLimit("daily", 2000), // 每日限额            ["wechat/menu_create"]  = new HttpQuotaLimit("weekly", 1000),   // 每周限额            ["wechat/upload_media"] = new HttpQuotaLimit("monthly", 50000), // 每月限额            ["wechat/lifetime_stat"] = new HttpQuotaLimit("lifetime", 10000)    // 生命周期总额        };    });// 配置特定客户端services.AddHttpClient("weixin")    .ConfigureOptions(options =>    // 或使用重载:.ConfigureOptions((options, serviceProvider) =>    {        options.QuotaLimits = new Dictionary<string, HttpQuotaLimit>        {            ["wechat/accesstoken"] = new HttpQuotaLimit("daily", 2000), // 每日限额            ["wechat/menu_create"]  = new HttpQuotaLimit("weekly", 1000),   // 每周限额            ["wechat/upload_media"] = new HttpQuotaLimit("monthly", 50000), // 每月限额            ["wechat/lifetime_stat"] = new HttpQuotaLimit("lifetime", 10000)    // 生命周期总额        };    });

3. 为请求指定配额键#

在发送请求时,使用 SetQuotaKey 关联到对应的配额配置,使该请求受对应规则的约束:

cs
var response = await httpRemoteService.SendAsync(    HttpRequestBuilder.Get("https://api.weixin.qq.com/cgi-bin/token")        .SetHttpClientName("weixin")        .SetQuotaKey("wechat/accesstoken"));    // 此键限制为每日 2000 次

内置配额策略#

框架内置了四种常用策略,通过 Strategy 属性指定(大小写不敏感):

策略名称说明窗口重置规则(基于 UTC 时间)
daily每日限额每天 00:00:00 重置
weekly每周限额每周一 00:00:00 重置
monthly每月限额每月第一天 00:00:00 重置
lifetime生命周期总额(不按时间重置)永不重置,达到上限后永久拒绝

例如,配置 Strategy = "daily"MaxCount = 2000 表示每天最多调用 2000 次。配置 Strategy = "lifetime"MaxCount = 10000 表示该配额键在整个应用生命周期内最多调用 10000 次,不会随时间重置。

自定义配额策略#

您可以实现 IHttpQuotaStrategy 接口来创建任意重置规则的策略(例如按小时、按自定义时间窗口、基于滑动窗口等)。

1. 定义策略类

cs
public sealed class HourlyQuotaStrategy : IHttpQuotaStrategy{    /// <inheritdoc />    public string Name => "hourly"; // 策略的唯一名称    /// <inheritdoc />    public bool TryAcquire(HttpQuotaCounter quotaCounter, int maxCount, out int current)    {        // 以 UTC 当前小时作为窗口标识(格式:yyyy-MM-dd HH)        var hourKey = DateTime.UtcNow.ToString("yyyy-MM-dd HH");        // 如果窗口标识改变,说明进入新的小时,重置计数        if (quotaCounter.WindowKey != hourKey)        {            quotaCounter.Count = 0;            quotaCounter.WindowKey = hourKey;        }        // 递增计数        quotaCounter.Count++;        current = quotaCounter.Count;        return current <= maxCount;    }}

2. 注册自定义配额策略

Startup.csProgram.cs 文件中注册自定义配额策略:

cs
services.AddHttpRemote(builder =>{    builder.AddQuotaStrategy<HourlyQuotaStrategy>();});

注册后,即可在 QuotaLimits 中使用 Strategy = "hourly"

cs
options.QuotaLimits = new Dictionary<string, HttpQuotaLimit>{    ["some/high_freq_api"] = new HttpQuotaLimit("hourly", 100)  // 每小时最多调用 100 次};

多节点集群部署#

默认情况下,配额管理器 HttpQuotaManager 基于内存缓存维护计数器,适用于单节点或单实例部署。在多节点集群环境中,每个节点各自维护独立的计数状态,导致整体配额限制失效(例如某接口全局限额 2000 次/天,各节点可能分别调用 2000 次而未互相感知)。为使配额在所有节点间准确共享,可将计数器存储迁移至分布式缓存(如 Redis)。

只需实现 IHttpQuotaManager 接口,将计数和窗口检查逻辑依托于分布式原子操作,然后替换默认服务即可:

cs
public class RedisHttpQuotaManager : IHttpQuotaManager{    // 实现接口方法,基于 Redis 实现原子递增、窗口重置和超限检查}

在服务注册时替换默认实现:

cs
services.Replace(ServiceDescriptor.Singleton<IHttpQuotaManager, RedisHttpQuotaManager>());

替换后,所有节点共享同一份配额计数器,确保集群全局的调用次数始终在配置的限制之内。自定义实现时,务必保证窗口重置与计数递增的原子性,避免并发导致超限。


通过以上机制,您可以轻松地为不同接口配置差异化的调用限额,有效防止因过度调用而触发的第三方 API 限制或费用超额问题。