はじめに
普段のお仕事では「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を叩く
- rate-limit-3
- 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
にサブスクリプションキーを指定
- サブスクリプション単位でリクエスト数を計上する
- ポリシーに書いた回数制限と異なる動作をすることもあるので注意!
- (仕組み的には おそらく期待通りの動作なのですが、ポリシーを使う側の人間からすると、期待と違う動作となります)
所感
- このような細かい挙動は、公式ドキュメントとしても載せきれないのが現状です。
- そのため、実際にポリシーを利用する場合には 余裕を持って事前に検証する のが吉です!