APIM ポリシー rate-limit と rate-limit-by-key の挙動調査
はじめに
普段のお仕事では「API 周りの面倒事アレコレ」を Azure API Management を使って解消しています。 その際に調査した内容について、ネタになるのでは?と思ったのでこちらの記事で共有いたします。
アクセスを制限するポリシー
API Management では APIに対するポリシーをXMLとして定義することで「APIへのアクセス制限」や「APIから返ってきたレスポンス加工」といった機能を付与できます。 個人的に 最も頻繁に 使われるのでは…?と思うのが rate-limit, rate-limit-by-key といった 「回数」でアクセスを制限するポリシー です。 (似たようなポリシーに quota, quota-by-key というポリシーがありますが、こちらは「容量」によるアクセス制限です)
rate-limit
公式ドキュメント:
要点:
サブスクリプション単位で アクセス回数制限します
「回数」や「制限解除までの秒数」が指定できます
アクセス制限に引っかかると 429 Too Many Requests を返します
ポリシー例: 「サブスクリプションキーごとに3コールまで許可、5秒後に制限が解除される」の意味 <policies> <inbound> <base /> <rate-limit calls="3" renewal-period="5" /> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
rate-limit-by-key
公式ドキュメント:
要点:
任意のキーで アクセス回数制限します
「回数」や「制限解除までの秒数」が指定できます(他にも計上する条件も指定できます)
アクセス制限に引っかかると 429 Too Many Requests を返します
ポリシー例: 「IPアドレスごとに3コールまで許可、5秒後に制限が解除される」の意味 <policies> <inbound> <base /> <rate-limit-by-key calls="3" renewal-period="5" counter-key="@(context.Request.IpAddress)" /> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
疑問
もっともシンプルな利用ケースとしては、「サブスクリプションキーごとに利用可能な回数を設ける」といったものかと思います。 では、以下の2つのポリシーは同じ挙動を示すのでしょうか?
<inbound>
<base />
<rate-limit-by-key calls="3" renewal-period="5" counter-key="@(context.Subscription.Id)" />
</inbound>
<inbound>
<base />
<rate-limit calls="3" renewal-period="5" />
</inbound>
実際に実験してみました。
実験手順
製品を一つ作り、製品に紐付いたサブスクリプションキーを発行する
例: Sample という製品を作成して、サブスクリプションキーを発行
以下のAPIを作る
rate-limit-3
rate-limit ポリシーで calls=3 とする
rate-limit-5
rate-limit ポリシーで calls=5 とする
rate-limit-by-key-3
rate-limit-by-key ポリシーで calls=3 とする
rate-limit-by-key-5
rate-limit-by-key ポリシーで calls=5 とする
※ オペレーションは以下だけ追加しておく(最低限、APIとして叩けるようにする)
メソッド:GET
オペレーション名:op
パス:/
※ All operations に対して、ポリシーを記述する
※ renewal-period は 10 秒としておく
※ すべてのAPIを 1. で作成した製品に紐づけておく
※ すべて同一のサブスクリプションキーでAPIを叩く
JMeter で以下の様なジョブを作成する
はじめに rate-limit-3 と rate-limit-5 を 10回ずつ 交互に叩く
余裕を持って 15秒待つ(下図の 2つ目のスレッドグループ設定に注意!)
スケジューラ にチェックを入れ
起動遅延 を 15 とし、
持続時間は 100 としておく
つぎに rate-limit-by-key-3 と rate-limit-by-key-5 を 10回ずつ 交互に叩く
JMeter を実行し、結果を確認する
結果
以下の通りとなりました。 同じような、サブスクリプション別アクセス回数制限ポリシーを設定しても、挙動が微妙に異なるようです。
rate-limit の挙動
まずはじめに上画像の左側に注目します。3, 5, 3, 5, ・・・ と交互にAPIが叩かれる中、
7件目、3回制限のAPIの4回目のリクエストで × 429 Too many requestsとなりました。
これは 3回制限をかけているので、4回目はアウトとなる、期待した通りの動作ですね。
以降、すべて 3回制限の方は × となっています
12件目、5回制限APIの6回目のリクエストで × 429 Too many requestsとなりました。
こちらも同様に、5回制限なので6回目以降はアウトになる、期待通りの動作ですね。
rate-limit に関しては、同一サブスクリプションキーを利用していても API別にリクエスト数が計上 され、ポリシーに設定した通りの回数で制限してくれるようです。
rate-limit-by-key の counter-key属性にサブスクリプションキーを指定した場合の挙動
上画像の右側に注目します。「rate-limit-by-key-3 にリクエスト」から数えて、
5件目、3回制限のAPIの3回目のリクエストですでに × 429 Too many requestsとなってしまいました
8件目、5回制限のAPIの4回目のリクエストですでに × 429 Too many requestsとなってしまいました
上記結果は、ポリシーを設定した側の感覚として とても違和感のある挙動 です。
・・・が、同一サブスクリプションキーを使用していること を思い出しますと、以下の考えに行き着きました。
rate-limit-by-key では、純粋に counter-key に与えられた文字列だけで比較し
同一サブスクリプションキーを使用している場合は、API別にリクエスト数を計上せず、サブスクリプション別で計上しているのでは?
1件ずつ紐解いていくと・・・
4件目:サブスクリプションキー単位で すでに リクエスト数は4件 となっている
5件目:3回制限のAPI → API単位では 3件目だが、サブスクリプションキー単位では すでに 4件なので ×
6件目:サブスクリプションキー単位で リクエスト数は 6件
7件目:サブスクリプションキー単位で リクエスト数は 7件
8件目:5回制限のAPI → API単位では 4件目だが、サブスクリプションキー単位では すでに 8件なので ×
まとめ
前提条件
同一サブスクリプションキーを使用します
同一製品上のAPIを叩きます
rate-limit
API単位でリクエスト数を計上する
ポリシーに書いた回数制限通りの動作をしてくれる
→ サブスクリプションキーで制限をかけたい場合はこちらが誤解なく使えて便利?
rate-limit-by-key で counter-key にサブスクリプションキーを指定
サブスクリプション単位でリクエスト数を計上する
ポリシーに書いた回数制限と異なる動作をすることもあるので注意!
(仕組み的には おそらく期待通りの動作なのですが、ポリシーを使う側の人間からすると、期待と違う動作となります)
所感
このような細かい挙動は、公式ドキュメントとしても載せきれないのが現状です。
そのため、実際にポリシーを利用する場合には 余裕を持って事前に検証する のが吉です!