2011-07-22 16 views
9

どのように私はこれについて行くかもしれない考えがありますか?オンラインで情報を見つけるのはかなり困難です。私が見つけた最高の宝石は、それは宝石ですが、私はそのアプリケーションを実装する方法を考えることができます。ユーザーあたりの関数呼び出しのレート制限を

答えて

8

これは、1)Webサーバー2)ラックアプリケーションで処理できます。すべてあなたが必要とするものに依存します。我々use内蔵nginxの機能をAPIリクエストを制限する:

 limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s; 
    limit_req zone=one burst=2; 

別の解決策はrack-throttleです。

これは、ラックアプリケーションに着信HTTP要求をレート制限するためのロジックを提供するラックミドルウェアです。 Rack :: Throttleは、Ruby on Rails 3.0やSinatraなど、RackベースのRuby Webフレームワークで使用できます。

+0

IPアドレスのリクエストゾーン部分ですか?これがユーザーごとであるかどうか疑問に思っていました。 –

2

ここでは、Redisとタイムスタンプを使用して実装する方法の例を示します。このモジュールをuser.rbに組み込み、user.allowed_to?(:reveal_email)

# Lets you limit the number of actions a user can take per time period 
# Keeps an integer timestamp with some buffer in the past and each action increments the timestamp 
# If the counter exceeds Time.now the action is disallowed and the user must wait for some time to pass. 

module UserRateLimiting 

    class RateLimit < Struct.new(:max, :per) 
    def minimum 
     Time.now.to_i - (step_size * max) 
    end 

    def step_size 
     seconds = case per 
     when :month then 18144000 # 60 * 60 * 24 * 7 * 30 
     when :week then 604800 # 60 * 60 * 24 * 7 
     when :day then 86400 # 60 * 60 * 24 
     when :hour then 3600  # 60 * 60 
     when :minute then 60 
     else raise 'invalid per param (day, hour, etc)' 
     end 
     seconds/max 
    end 
    end 

    LIMITS = { 
    :reveal_email => RateLimit.new(200, :day) 
    # add new rate limits here... 
    } 

    def allowed_to? action 
    inc_counter(action) < Time.now.to_i 
    end 

    private 

    def inc_counter action 
    rl = LIMITS[action] 
    raise "couldn't find that action" if rl.nil? 
    val = REDIS_COUNTERS.incrby redis_key(action), rl.step_size 
    if val < rl.minimum 
     val = REDIS_COUNTERS.set redis_key(action), rl.minimum 
    end 
    val.to_i 
    end 

    def redis_key action 
    "rate_limit_#{action}_for_user_#{self.id}" 
    end 

end 
+0

ちょうど頭が上がっています:「月」の計算が正しくありません。 「* 7」因子は必要ありません。さらに、 '1.month.to_i'を使って1ヶ月以内の秒数を得ることができます(他のユニットと同様に)。 –

+0

ありがとうございます!とても有難い。 –