ASP.NET MVCでリクエストスロットリングを実装する最良の方法は?


212

特定の期間にユーザーの操作を抑制するさまざまな方法を実験しています。

  • 質問/回答の投稿を制限する
  • 編集を制限する
  • フィードの取得を制限する

とりあえず、キャッシュを使用してユーザーアクティビティのレコードを挿入しています。ユーザーが同じアクティビティを行った場合にそのレコードが存在する場合は、スロットルします。

キャッシュを使用すると、古いデータのクリーニングとユーザーのアクティビティウィンドウのスライドが自動的に行われますが、それがどのようにスケーリングされるかが問題になる可能性があります。

リクエスト/ユーザーアクションを効果的に抑制できるようにする他の方法は何ですか(安定性を重視)。


ユーザーごとまたは質問ごとに制限しようとしていますか?ユーザーごとの場合、セッションを使用できますが、これはより小さなセットになります。
Greg Ogle

1
ユーザーごとですが、Cookieが必要なため、セッションを使用できませんでした。現在、IPアドレスに基づいて制限しています。
ジャロッドディクソン

1
現在、MVCページの場合はnugetパッケージgithub.com/stefanprodan/MvcThrottleを、ウェブAPIリクエストの場合github.com/stefanprodan/WebApiThrottleを検討してください
Andy

回答:


240

過去1年間、Stack Overflowで使用していたものの一般的なバージョンは次のとおりです。

/// <summary>
/// Decorates any MVC route that needs to have client requests limited by time.
/// </summary>
/// <remarks>
/// Uses the current System.Web.Caching.Cache to store each client request to the decorated route.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ThrottleAttribute : ActionFilterAttribute
{
    /// <summary>
    /// A unique name for this Throttle.
    /// </summary>
    /// <remarks>
    /// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-192.168.0.1"
    /// </remarks>
    public string Name { get; set; }

    /// <summary>
    /// The number of seconds clients must wait before executing this decorated route again.
    /// </summary>
    public int Seconds { get; set; }

    /// <summary>
    /// A text message that will be sent to the client upon throttling.  You can include the token {n} to
    /// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
    /// </summary>
    public string Message { get; set; }

    public override void OnActionExecuting(ActionExecutingContext c)
    {
        var key = string.Concat(Name, "-", c.HttpContext.Request.UserHostAddress);
        var allowExecute = false;

        if (HttpRuntime.Cache[key] == null)
        {
            HttpRuntime.Cache.Add(key,
                true, // is this the smallest data we can have?
                null, // no dependencies
                DateTime.Now.AddSeconds(Seconds), // absolute expiration
                Cache.NoSlidingExpiration,
                CacheItemPriority.Low,
                null); // no callback

            allowExecute = true;
        }

        if (!allowExecute)
        {
            if (String.IsNullOrEmpty(Message))
                Message = "You may only perform this action every {n} seconds.";

            c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) };
            // see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
        }
    }
}

使用例:

[Throttle(Name="TestThrottle", Message = "You must wait {n} seconds before accessing this url again.", Seconds = 5)]
public ActionResult TestThrottle()
{
    return Content("TestThrottle executed");
}

ASP.NETキャッシュは、ここでのチャンプのように機能します。これを使用することで、スロットルエントリの自動クリーンアップを取得できます。また、トラフィックが増加しているため、これがサーバーの問題であることはわかりません。

この方法についてフィードバックをお寄せください。スタックオーバーフローを改善すると、Ewokの修正がさらに速くなります。


5
簡単な質問-キーの一部としてc.HttpContext.Request.UserHostAddress値を使用しています。その値は空またはnull、あるいはすべて同じ値にすることができますか?(つまり、ロードバランサーを使用していて、そのマシンのIPである場合は、実際のクライアントではありません)同様に、プロキシまたはロードバランサー(つまり、BIG IP F5)で同じデータをそこに配置し、確認する必要があります。 X-Forwarded-Forも何かのために?
Pure.Krome

7
@ Pure.Krome-はい、可能性があります。クライアントIPを取得するときは、REMOTE_ADDRおよびHTTP_X_FORWARDED_FORサーバー変数の両方をチェックし、適切にサニタイズするヘルパー関数を使用します。
ジャロッドディクソン

3
@BrettRobi、ユーザーのIPアドレスに基づいたサーバーアフィニティがあると確信しています。したがって、彼らはおそらく同じサーバーを攻撃するでしょう。
mmcdole 2011

4
気にしてコメントストリームでこれまで読んだことがある人のために...リダイレクトする前にスロットルキャッシュキーをクリアする独自のリダイレクトを作成することになりました。このように、すべてのリダイレクトはコードを通過してキーを削除しますが、どのリダイレクトもThrottle属性をトリガーしません。
SLoret 14

4
あなたがこのウェブAPIバージョンを探している場合は、ここで確認してください。stackoverflow.com/questions/20817300/...
パパブルゴーニュ

68

マイクロソフトには、IIS 7.0の動的IP制限拡張機能と呼ばれるIIS 7の新しい拡張機能があります-ベータ版。

「IIS 7.0の動的IP制限は、サービス拒否攻撃とWebサーバーおよびWebサイトへのブルートフォース攻撃に対する保護を提供するモジュールです。このような保護は、異常に多くの同時要求を行うHTTPクライアントのIPアドレスを一時的にブロックすることで提供されます。または、短期間に大量のリクエストを行うユーザー。」 http://learn.iis.net/page.aspx/548/using-dynamic-ip-restrictions/

例:

後にブロックするように基準を設定した場合、X requests in Y millisecondsまたはX concurrent connections in Y millisecondsIPアドレスがブロックされるY milliseconds場合、要求は再度許可されます。


1
それがGooglebotのようなクローラーで問題を引き起こしたかどうか知っていますか?
エレファント2013


1
バージョン8の時点でリリースされ、IISにバンドルされています-iis.net/learn/get-started/whats-new-in-iis-8/…–
Matthew

これを使用したいのですが、でスロットルすることはできません<location>。それはアプリのすべてのリクエストか、それともなしです。
Kasey Speakman 2017

すべてのトラフィックが同じIPアドレスから送信されているように見えるため、Webサーバーがロードバランサーの背後にある場合は役に立ちません。明らかなものがない場合を除いて...
Dscoduc

11

私たちはこのURL http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspxから借用した手法を使用しますこれはスロットルではなく、貧しい人のサービス拒否(DOS)に使用します。これもキャッシュベースであり、あなたがやっていることに似ているかもしれません。DOS攻撃を防ぐためにスロットルしていますか?ルーターは確かにDOSを削減するために使用できます。ルーターが必要なスロットリングを処理できると思いますか?


1
それは、私たちがすでにやっていることのほとんどです-しかし、それは素晴らしい働きをしています:)
ジャロッド・ディクソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.