Azure API Management中为不同API密钥配置差异化速率限制的方法
你最初使用的按订阅密钥(API密钥)做计数的rate-limit-by-key策略本身逻辑是正确的,现存的问题是策略里的calls(允许调用次数)和renewal-period(计数重置周期)是硬编码的固定值,所有使用不同API密钥的客户端都会共享同一套限额。只要把这两个参数改成按当前订阅ID动态匹配预存的规则值,就能实现不同API密钥对应专属限额的需求,不需要额外引入外部组件。
核心实现逻辑:APIM的策略表达式支持直接读取内置配置存储的键值对,完全可以把「API密钥(对应唯一订阅ID)-限速规则」的映射关系存在APIM内部,不需要把规则硬编码到策略逻辑里。
1. 配置规则映射的存储
用APIM内置的**命名值(Named Values)**做规则存储就够用,不需要接外部数据库:
- 新建两个明文类型的命名值,分别存两类规则:
SubRate_Calls:存每个订阅ID对应的允许调用次数,用JSON结构存储SubRate_Period:存每个订阅ID对应的计数重置周期(单位:秒),同样用JSON结构存储
举个匹配你业务场景的实际配置示例:
// SubRate_Calls 内容:对应两个客户的规则 { "premium_sub_1001": 1, "basic_sub_2002": 1 } // SubRate_Period 内容:对应两个客户的规则 { "premium_sub_1001": 60, "basic_sub_2002": 600 }
这个配置直接对应你的业务要求:高级订阅1分钟(60秒)允许调用1次,基础订阅10分钟(600秒)允许调用1次。
2. 替换原有硬编码的限速策略
把原来写死参数的rate-limit-by-key策略(即你最初参考的写法:<rate-limit-by-key calls="3" renewal-period="15" counter-key="@(context.Subscription.Id)" />),改成动态读取上面存的规则值,加兜底逻辑避免未配置的订阅直接报错:
<rate-limit-by-key calls="@( // 解析存的调用次数规则,按当前请求的订阅ID匹配对应值,匹配不到默认给1次的基础限额 JObject.Parse(NamedValue.GetValue("SubRate_Calls", false))[context.Subscription.Id]?.Value<int>() ?? 1 )" renewal-period="@( // 解析存的周期规则,按当前请求的订阅ID匹配对应值,匹配不到默认给600秒(10分钟)的重置周期 JObject.Parse(NamedValue.GetValue("SubRate_Period", false))[context.Subscription.Id]?.Value<int>() ?? 600 )" counter-key="@(context.Subscription.Id)" />
这个策略放到你需要做限速的API的入站处理流程里就行,不需要改后端API的任何代码。
3. 日常运维的操作逻辑
后续所有的规则调整都不需要修改核心策略代码,只需要更新两个命名值里的JSON配置即可:
- 新客户开通套餐:只要在两个JSON里加上该客户的订阅ID、对应的调用次数和周期值,规则即时生效
- 调整套餐限额:比如要把高级客户的限额从1分钟1次改成1分钟3次,只需要把对应订阅ID在
SubRate_Calls里的值改成3就行,全链路无感知 - 临时调整单个客户的限额:单独修改JSON里对应该客户的单条记录,不会影响其他客户的现有规则
注意事项
- 存规则用的命名值选明文类型即可,不要开加密标记,否则会因为密文解析失败导致策略报错
- 如果后续需要按不同API路径做更细的限速,只需要把
counter-key的拼接逻辑改成@($"{context.Subscription.Id}-{context.Request.Url.Path}"),对应的规则JSON里的键也同步拼接对应路径和订阅的组合值就行,核心动态取值的逻辑不用调整
内容的提问来源于stack exchange,提问作者Justin Mathew

