WebApiThrottle限流框架使用手冊


閱讀目錄:

  1. 介紹
  2. 基於IP全局限流
  3. 基於IP的端點限流
  4. 基於IP和客戶端key的端點限流
  5. IP和客戶端key的白名單
  6. IP和客戶端key自定義限制頻率
  7. 端點自定義限制頻率
  8. 關於被拒請求的計數器
  9. 在web.config或app.config中定義限制策略
  10. 獲取API的客戶端key
  11. 存儲限流的數據
  12. 運行期間更新限制頻率
  13. 限流的請求日志
  14. 用ThrottlingFilter、EnableThrottlingAttribute特性配置限制頻率
  15. 關於ThrottlingMiddleware限制頻率

介紹

為了防止網站意外暴增的流量比如活動、秒殺、攻擊等,導致整個系統癱瘓,在前后端接口服務處進行流量限制是非常有必要的。本篇主要介紹下Net限流框架WebApiThrottle的使用。

WebApiThrottle是一個專門為webApi限制請求頻率而設計的,支持寄宿OWIN上的中間件的限制過濾。服務端接口可以基於客戶端請求IP地址、客戶端請求key、及請求路由去限制webapi接口的訪問頻率。

使用nuget命令安裝WebApiThrottle:

PM> Install-Package WebApiThrottle

Nuget地址:

https://www.nuget.org/packages/WebApiThrottle/

WebApiThrottle支持自定義配置各種限流策略。可以根據不同場景配置多個不同的限制,比如授權某個IP每秒、每分鍾、每小時、每天、每周的最大調用次數。 這些限制策略可以配置在所有請求上,也可以單獨給每個API接口去配置。

基於IP全局限流

下面的代碼是限制來自同IP請求的最大次數。如果在一分鍾內,同樣IP的客戶端分別調用api/values和api/values/1兩個接口, 那么調用api/values/1的請求會被拒絕掉。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MessageHandlers.Add(new ThrottlingHandler()
        {
            Policy = new ThrottlePolicy(perSecond: 1, perMinute: 20, perHour: 200, perDay: 1500, perWeek: 3000)
            {
                IpThrottling = true
            },
            Repository = new CacheRepository()
        });
    }
}

如果是自寄宿在Owin上的WebApi,上面的CacheRepository必須替換成運行時的MemoryCacheRepository類,因為CacheRepository使用的是Asp.net版本的緩存。

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        // Configure Web API for self-host. 
        HttpConfiguration config = new HttpConfiguration();

        //Register throttling handler
        config.MessageHandlers.Add(new ThrottlingHandler()
        {
            Policy = new ThrottlePolicy(perSecond: 1, perMinute: 20, perHour: 200, perDay: 1500, perWeek: 3000)
            {
                IpThrottling = true
            },
            Repository = new MemoryCacheRepository()
        });

        appBuilder.UseWebApi(config);
    }
}

基於IP的端點限流

上面的api/values限流配置會對整個api/values開頭的API限流,同一秒內、同一ip訪問api/values后,所有后續訪問api/values/xxx的請求都會被拒絕掉。 如果配置了端點限流,同一秒內你也訪問api/values/1了,請求將不會被拒絕,因為它們走的是不同的路由。

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = new ThrottlePolicy(perSecond: 1, perMinute: 30)
    {
        IpThrottling = true,
        EndpointThrottling = true
    },
    Repository = new CacheRepository()
});

基於IP和客戶端key的端點限流

如果同一個ip的客戶端,在同一秒內,調用了2次api/values,其最后一次的調用將會被拒絕掉。

如果想接口通過唯一key去識別限制客戶端,忽略客戶端的ip地址限制,應該配置IpThrottling為false。

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = new ThrottlePolicy(perSecond: 1, perMinute: 30)
    {
        IpThrottling = true,
        ClientThrottling = true,
        EndpointThrottling = true
    },
    Repository = new CacheRepository()
});

IP和客戶端key的白名單

如果請求是從一個白名單中的IP或客戶端key發起的,那么限流策略將不會生效,這個請求的所有信息也不會被存儲。 其IP白名單列表支持IP v4和v6的范圍配置,比如"192.168.0.0/24", "fe80::/10" 和 "192.168.0.0-192.168.0.255",關於IP范圍的更多信息請查看https://github.com/jsakamoto/ipaddressrange

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = new ThrottlePolicy(perSecond: 2, perMinute: 60)
    {
        IpThrottling = true,
        IpWhitelist = new List<string> { "::1", "192.168.0.0/24" },

        ClientThrottling = true,
        ClientWhitelist = new List<string> { "admin-key" }
    },
    Repository = new CacheRepository()
});

IP和客戶端key自定義限制頻率

你可以自定義基於ip或客戶端key的請求頻率限制,這些限制會重寫WebApiThrottle的默認配置。

需要注意的是,這些自定義策略需要寫到全局配置里才會生效,策略里可以單獨給某個ip或某個key配置限流策略。

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = new ThrottlePolicy(perSecond: 1, perMinute: 20, perHour: 200, perDay: 1500)
    {
        IpThrottling = true,
        IpRules = new Dictionary<string, RateLimits>
        { 
            { "192.168.1.1", new RateLimits { PerSecond = 2 } },
            { "192.168.2.0/24", new RateLimits { PerMinute = 30, PerHour = 30*60, PerDay = 30*60*24 } }
        },

        ClientThrottling = true,
        ClientRules = new Dictionary<string, RateLimits>
        { 
            { "api-client-key-1", new RateLimits { PerMinute = 40, PerHour = 400 } },
            { "api-client-key-9", new RateLimits { PerDay = 2000 } }
        }
    },
    Repository = new CacheRepository()
});

端點自定義限制頻率

你也可以為明確的路由地址去自定義限制頻率,這些限制配置會重寫WebApiThrottle的默認配置。也可以通過相關聯的路由地址去定義端點的限制規則,比如api/entry/1端點的請求僅僅是/entry/整個路由地址請求的一部分。 配置后,端點限制引擎會在請求的絕對URI中去搜索這個表達式(api/entry/1),如果這個表達式在請求路由策略中被找到,那么這個限制規則將會被應用。如果有兩個或更多的限制規則匹配到同一個URL,更近一級的限制策略將會被應用。

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = new ThrottlePolicy(perSecond: 1, perMinute: 20, perHour: 200)
    {
        IpThrottling = true,
        ClientThrottling = true,
        EndpointThrottling = true,
        EndpointRules = new Dictionary<string, RateLimits>
        { 
            { "api/search", new RateLimits { PerSecond = 10, PerMinute = 100, PerHour = 1000 } }
        }
    },
    Repository = new CacheRepository()
});

關於被拒請求的計數器

默認情況下,被拒絕的請求不會累加到WebApiThrottle的計數器里。 比如一個客戶端在同一秒中請求了3次,而你配置的限制策略是每秒1次,那么分鍾、小時、天的計數器只會記錄第一次調用,因為第一次請求不會被拒絕。如果你想把被拒絕的請求也計算到其他的計數器里(分鍾、小時、天),你可以設置StackBlockedRequests為true。

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = new ThrottlePolicy(perSecond: 1, perMinute: 30)
    {
        IpThrottling = true,
        ClientThrottling = true,
        EndpointThrottling = true,
        StackBlockedRequests = true
    },
    Repository = new CacheRepository()
});

在web.config或app.config中定義限制策略

在web.config或app.config中配置限制策略,通過ThrottlePolicy.FromStore加裝配置項。

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = ThrottlePolicy.FromStore(new PolicyConfigurationProvider()),
    Repository = new CacheRepository()
});

配置示例(policyType的值1代表ip、2代表clientkey、3代表端點)

<configuration>

  <configSections>
    <section name="throttlePolicy" 
             type="WebApiThrottle.ThrottlePolicyConfiguration, WebApiThrottle" />
  </configSections>

  <throttlePolicy limitPerSecond="1"
                  limitPerMinute="10"
                  limitPerHour="30"
                  limitPerDay="300"
                  limitPerWeek ="1500"
                  ipThrottling="true"
                  clientThrottling="true"
                  endpointThrottling="true">
    <rules>
      <!--Ip 規則-->
      <add policyType="1" entry="::1/10"
           limitPerSecond="2"
           limitPerMinute="15"/>
      <add policyType="1" entry="192.168.2.1"
           limitPerMinute="12" />
      <!--Client 規則-->
      <add policyType="2" entry="api-client-key-1"
           limitPerHour="60" />
      <!--Endpoint 規則-->
      <add policyType="3" entry="api/values"
           limitPerDay="120" />
    </rules>
    <whitelists>
      <!--Ip 白名單-->
      <add policyType="1" entry="127.0.0.1" />
      <add policyType="1" entry="192.168.0.0/24" />
      <!--Client 白名單-->
      <add policyType="2" entry="api-admin-key" />
    </whitelists>
  </throttlePolicy>
</configuration>

獲取API的客戶端key

默認情況下,WebApiThrottle的ThrottlingHandler(限流處理器)會從客戶端請求head里通過Authorization-Token key取值。如果你的API key存儲在不同的地方,你可以重寫ThrottlingHandler.SetIndentity方法,指定你自己的取值策略。

public class CustomThrottlingHandler : ThrottlingHandler
{
    protected override RequestIdentity SetIndentity(HttpRequestMessage request)
    {
        return new RequestIdentity()
        {
            ClientKey = request.Headers.Contains("Authorization-Key") ? request.Headers.GetValues("Authorization-Key").First() : "anon",
            ClientIp = base.GetClientIp(request).ToString(),
            Endpoint = request.RequestUri.AbsolutePath.ToLowerInvariant()
        };
    }
}

存儲限流的數據

WebApiThrottle會在內存中存儲所有的請求數據,寄宿在IIS里使用ASP.NET版本的cache、自寄宿在Owin上使用運行時版本的緩存MemoryCache。如果你想改變請求數據存儲的策略,框架是支持redis、nosql、數據庫存儲的,這種情況下必須創建自己的存儲引擎,可以通過實現IThrottleRepository接口完成。

public interface IThrottleRepository { bool Any(string id);

ThrottleCounter? FirstOrDefault(string id);

void Save(string id, ThrottleCounter throttleCounter, TimeSpan expirationTime);

void Remove(string id);

void Clear();
}

自從1.2版本后有IPolicyRepository的接口可以實現存儲、獲取限制策略對象,意味着可以持久化限流策略,同時也可以被用於在運行期間動態更新限制策略對象。

public interface IPolicyRepository
{
    ThrottlePolicy FirstOrDefault(string id);

    void Remove(string id);

    void Save(string id, ThrottlePolicy policy);
}

運行期間更新限制頻率

為了更新限制策略對象,並在運行時使用新的ThrottlingHandler對象,需要引入WebApiThrottle 1.2版本后支持的ThrottleManager.UpdatePolicy函數。

在啟動時注冊ThrottlingHandler對象,並在構造函數中傳入PolicyCacheRepository ,如果你是通過Owin自寄宿的webapi,需要使用PolicyMemoryCacheRepository對象。

public static void Register(HttpConfiguration config)
{
    //trace provider
    var traceWriter = new SystemDiagnosticsTraceWriter()
    {
        IsVerbose = true
    };
    config.Services.Replace(typeof(ITraceWriter), traceWriter);
    config.EnableSystemDiagnosticsTracing();

    //添加限流處理者到Web API消息處理集合里
    config.MessageHandlers.Add(new ThrottlingHandler(
        policy: new ThrottlePolicy(perMinute: 20, perHour: 30, perDay: 35, perWeek: 3000)
        {
            //啟用ip限制策略
            IpThrottling = true,

            //啟用客戶端key限制策略
            ClientThrottling = true,
            ClientRules = new Dictionary<string, RateLimits>
            { 
                { "api-client-key-1", new RateLimits { PerMinute = 60, PerHour = 600 } },
                { "api-client-key-2", new RateLimits { PerDay = 5000 } }
            },

            //啟用端點限制策略
            EndpointThrottling = true
        },

        //如果是owin寄宿,替換成PolicyMemoryCacheRepository
        policyRepository: new PolicyCacheRepository(),

        //如果是owin寄宿,替換成MemoryCacheRepository 
        repository: new CacheRepository(),

        logger: new TracingThrottleLogger(traceWriter)));
}

當你想更新限制策略對象時,可以在任何你的代碼里調用靜態方法ThrottleManager.UpdatePolicy去刷新內存中的策略數據。

public void UpdateRateLimits()
{
    //初始化策略倉庫
    var policyRepository = new PolicyCacheRepository();

    //從緩存中獲取策略對象
    var policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey());

    //更新客戶端限制頻率
    policy.ClientRules["api-client-key-1"] =
        new RateLimits { PerMinute = 80, PerHour = 800 };

    //添加新的客戶端限制頻率
    policy.ClientRules.Add("api-client-key-3",
        new RateLimits { PerMinute = 60, PerHour = 600 });

    //應用策略更新
    ThrottleManager.UpdatePolicy(policy, policyRepository);

}

限流的請求日志

如果你想記錄限流后的請求日志,可以實現IThrottleLogger接口,添加到ThrottlingHandler里。

public interface IThrottleLogger
{
    void Log(ThrottleLogEntry entry);
}

實現ITraceWriter日志記錄接口的例子

public class TracingThrottleLogger : IThrottleLogger
{
    private readonly ITraceWriter traceWriter;

    public TracingThrottleLogger(ITraceWriter traceWriter)
    {
        this.traceWriter = traceWriter;
    }

    public void Log(ThrottleLogEntry entry)
    {
        if (null != traceWriter)
        {
            traceWriter.Info(entry.Request, "WebApiThrottle",
                "{0} Request {1} from {2} has been throttled (blocked), quota {3}/{4} exceeded by {5}",
                entry.LogDate, entry.RequestId, entry.ClientIp, entry.RateLimit, entry.RateLimitPeriod, entry.TotalRequests);
        }
    }
}

用SystemDiagnosticsTraceWriter和ThrottlingHandler記錄日志的使用例子:

var traceWriter = new SystemDiagnosticsTraceWriter()
{
    IsVerbose = true
};
config.Services.Replace(typeof(ITraceWriter), traceWriter);
config.EnableSystemDiagnosticsTracing();

config.MessageHandlers.Add(new ThrottlingHandler()
{
    Policy = new ThrottlePolicy(perSecond: 1, perMinute: 30)
    {
        IpThrottling = true,
        ClientThrottling = true,
        EndpointThrottling = true
    },
    Repository = new CacheRepository(),
    Logger = new TracingThrottleLogger(traceWriter)
});

用ThrottlingFilter、EnableThrottlingAttribute特性配置限制頻率

EnableThrottling與ThrottlingHandler是一個二選一的策略配置方案,二者會做同樣的事情,但ThrottlingHandler可以通過EnableThrottlingAttribute特性指定某個webapi的controllers和actions去自定義頻率限制。需要注意的是,在webapi請求管道中,ThrottlingHandler是在controller前面執行,因此在你不需要ThrottlingFilter提供的功能時,可以用ThrottlingHandler去直接替代它。

設置ThrottlingFilter過濾器的步驟,跟ThrottlingHandler類似:

config.Filters.Add(new ThrottlingFilter()
{
    Policy = new ThrottlePolicy(perSecond: 1, perMinute: 20, 
    perHour: 200, perDay: 2000, perWeek: 10000)
    {
        //ip配置區域
        IpThrottling = true,
        IpRules = new Dictionary<string, RateLimits>
        { 
            { "::1/10", new RateLimits { PerSecond = 2 } },
            { "192.168.2.1", new RateLimits { PerMinute = 30, PerHour = 30*60, PerDay = 30*60*24 } }
        },
        //添加127.0.0.1到白名單,本地地址不啟用限流策略
        IpWhitelist = new List<string> { "127.0.0.1", "192.168.0.0/24" },

        //客戶端配置區域,如果ip限制也是啟動的,那么客戶端限制策略會與ip限制策略組合使用。
        ClientRules = new Dictionary<string, RateLimits>
        { 
            { "api-client-key-demo", new RateLimits { PerDay = 5000 } }
        },
        //白名單中的客戶端key不會進行限流。
        ClientWhitelist = new List<string> { "admin-key" },

        //端點限制策略配置會從EnableThrottling特性中獲取。
        EndpointThrottling = true
    }
});

使用特性開啟限流並配置限制頻率:

[EnableThrottling(PerSecond = 2)]
public class ValuesController : ApiController
{
    [EnableThrottling(PerSecond = 1, PerMinute = 30, PerHour = 100)]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    [DisableThrotting]
    public string Get(int id)
    {
        return "value";
    }
}

關於ThrottlingMiddleware限制頻率

ThrottlingMiddleware是一個OWIN中間件部分,它的作用跟ThrottlingHandler一樣。使用ThrottlingMiddleware 你可以在webapi作用域范圍外配置限制策略,跟使用OAuth中間件或SignalR端點類似。

自寄宿配置例子:

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        ...

        //從app.config加載限流策略
        appBuilder.Use(typeof(ThrottlingMiddleware),
            ThrottlePolicy.FromStore(new PolicyConfigurationProvider()),
            new PolicyMemoryCacheRepository(),
            new MemoryCacheRepository(),
            null);

        ...
    }
}

IIS寄宿配置例子:

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        ...

    //從web.config加載限流策略
    appBuilder.Use(typeof(ThrottlingMiddleware),
        ThrottlePolicy.FromStore(new PolicyConfigurationProvider()),
        new PolicyCacheRepository(),
        new CacheRepository(),
        null);

        ...
    }
}

 

翻譯了https://github.com/stefanprodan/WebApiThrottle的官方文檔,希望對大家有所幫助。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM