ZAICOでは、Android・iOS・Rubyエンジニアを絶賛募集中です! 詳しくは、採用ページをご覧ください。
かなり久しぶりの投稿になります。
私用で来月6月中旬から約1ヶ月間ミャンマーのヤンゴンに行ってきます。
@fukata です。
ZAICOでは四半期に1回、FedEX Weekというものを行なっており、普段関わっていないタスクを自由に行えるイベントになります。これまでデプロイの自動化などをこのイベントを通して作ってきました
今回はRateLimit機能を実装してみました。
RateLimitとは
zaicoでは公開APIとして様々なAPIを提供しています。
https://zaicodev.github.io/zaico_api_doc/
これらのAPIを組み合わせてお客様のシステムと連携したりすることが出来るようになっています。
WEBアプリやモバイルアプリ内から呼ばれるAPIの実行頻度についてはサービス提供者側である程度コントロール可能ですが公開APIはお客様自身でプログラムを書いていただく性質上、こちらの想定を超えた呼び出し回数、頻度というのが起きやすくなります。
より多くのAPIを呼んでいただくのはありがたいですがサーバーのリソースも有限なため全てのお客様に極力公平に利用していただけるように想定を超えた呼び出しについては一定時間ブロックさせていただいております。
今まではAWS WAFを用いてRateLimitの処理を行なっておりましたがより柔軟に制御できるように今回、Rails側に処理を実装し、AWS WAFと併用する形になります。
今回、なぜRails側に実装したかについては後述しています。
やりたいこと
- 有料プランによって実行回数、頻度を設定したい。
- その上で特定のお客様のみ上限を個別に設定できるようにもしたい。
- APIのグループ毎にも設定したい。
というのが主な要件でした。
現在、zaicoでは公開APIが利用可能な有料プランとして「スタンダード」「ビジネス」の2つをご用意させていただいております。
料金内容に合った機能提供をさせていただきたいと思い、有料プラン別に実行回数・頻度を設定できるようにと運用しているとどうしても特別なケースというのがあるためそのような状況にもプラン全体の設定を変更せずに特定のお客様の設定のみ変更できるようにもする必要があります。
APIのグループ分けというのは公開APIによっては処理が軽いもの、重いものとサーバーのリソース使用率が異なります。それらを一緒くたに同じ設定値で処理すると柔軟にコントロールすることができなくなります。処理の軽いAPIと処理の重いAPIでさらにそれぞれ別々の上限を設定する必要もあります。
実装
RateLimitの処理自体は gcra-ruby を使用しました。リクエスト毎にチェックして上限に達していれば 429 Too Many Requests と返すという感じです。
APIのグループ分けについては下記のような感じでアクション毎にグループを指定出来るようにしました。(設定内容は実際の実装とは異なります)
class Api::V1::InventoriesController < Api::V1::BaseController rate_limit :light, %i(index show) rate_limit :medium, %i(create update) rate_limit :heavy, %i(destroy) end
特定のお客様のみの設定については管理画面から設定内容をRedisに登録出来るようにしています。
Rails以外の実装
RateLimitの目的はRailsなどアプリケーション・サーバーに余計な処理を実行させたくないためだと思います。
そのため、より多くのリクエストがありサーバーリソースを有効に活用したい場合には下記のような構成も考えられるかなと思います。
- nginx + lua-nginx-module
- h2o + mruby
- Go
現在、zaicoではインフラ構成を大幅に変更している最中ですので既存のAPIサーバー(Rails)に実装しましたがインフラ構成が刷新出来た際にはRateLimitの処理をRailsから移してみたいと思います。
リリース時期
今回実装したRateLimitのリリースについてはまだ検証段階なのですぐにリリースするというものではありませんがそのうちリリースされるかと思います。