只有累積,沒有奇蹟

2022年5月23日 星期一

[NETCore] ASP.NET Core 限流框架 AspNetCoreRateLimit 整合 Redis

前言
在上一篇介紹了 ASP.NET Core 中的限流框架 AspNetCoreRateLimit,紀錄使用者 Request 的 IP 作為限定流量的判斷來源,並將計數器的值存放在 Server 的 Memory 中,存放在 Memory 中如果 Server 數量單台的話會沒有問題,如果 Server 數量不只一台就需要共用的 Cache Server 來存放 Request 資訊,此時可以搭配 Redis 作為 Cache server 使用,讓所有 Server 在判斷限流時都具備相同的限制條件,這篇就紀錄 AspNetCoreRateLimit 整合 Redis 的操作與設定,若有問題或是錯誤的地方歡迎網路的高手大大給予指導

安裝 Redis 
首先,起手式先來安裝 Redis,想使用 Redis 可以參考之前的介紹文章

  • Window : 在 Windows 下載與安裝 Redis使用 Docker 安裝 Redis
  • Linux : 在 CentOS7 上安裝 Redis
  • 這裡直接使用 Docker 指令來起 Redis 服務,在 powershell 輸入下列指令
    > docker run --name redis-throttle -p 6379:6379 -d redis
    啟動成功會顯示 docker 的 container ID 資訊在畫面上,接著在輸入 docker ps -a 確認
    > docker ps -a
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS                    NAMES
    23e010857e85        redis               "docker-entrypoint.s…"   4 seconds ago         Up 2 seconds   0.0.0.0:6379->6379/tcp   redis-throttle
    
    Redis 啟動成功,對應的 Port 號為 6379

    設定 AspNetCoreRateLimit  
    在前一篇完整的介紹 AspNetCoreRateLimit 設定方式與代碼,這裡僅說明差異的地方,要使用 Redis Cache 可以使用  IDistributedCache ,IDistributedCache 在 ASP.NET Core 中命名空間為 Microsoft.Extensions.Caching.Redis,因此下一步是進行安裝的動作,在 Nuget Console 輸入下列指令
    Install-Package Microsoft.Extensions.Caching.Redis -Version 2.2.0
    接著到 startup.cs 將原本儲存在 memory 的地方,改成存到 Redis 並且定義 Redis 的連線字串內容
        public void ConfigureServices(IServiceCollection services)
        {
            // 省略
    
            // 注入 counter and IP Rules 
            //services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
            //services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
            services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
            services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
            services.AddDistributedRedisCache(option =>
            {
                option.Configuration = Configuration["Redis:ConnectionString"];
                option.InstanceName = Configuration["Redis:InstanceName"];
            });
        }
    代碼中定義了 Redis Server 的連線字串與名稱,Throttle 的 Rule 設定為 10m 僅能訪問 1次,因此我們需要到 appsettuing.json 加上 Redis 的設定資料
    "Redis": {
        "ConnectionString": "127.0.0.1:6379",
        "InstanceName": "Redis Throttle"
    },

    確認 Redis 資料 
    此時,在重新執行應用程式可以發現當達到設定的次數上限時,就會跳出我們熟悉阻擋的訊息
    API calls quota exceeded! maximum admitted 1 per 10m.
    再來使用 Redis Desktop Manager 連線到 Docker 內的 Redis,可以發現當達到上限次數時 Redis 有存放相關的 Request 資料,如下圖所示
    AspNetCoreRateLimit 將資料儲存在 Redis 成功,大功告成 !!!

    2022年5月9日 星期一

    [NETCore] ASP.NET Core 限流框架 AspNetCoreRateLimit

    前言
    開發者都知道系統上線後才是挑戰的開始,舉例來說像是每天不斷的有爬蟲程式來抓取網站資料,或是對外開放的 API 服務遭到攻擊事件,若沒有良好的防範機制很有可能造成 Server 因為攻擊無法正常服務,甚至引起雪崩效應影響到其他系統服務,在 ASP.NET Core 中可以透過  AspNetCoreRateLimit  框架根據 Request 的 IP 或是 ClientID 來達到限制流量的效果,這篇就來簡單分享一下有關 AspNetCoreRateLimit 的安裝與基本使用,若有問題或是錯誤的地方歡迎網路的高手大大給予指導

    安裝
    首先,為了方便大家更容易了解,建立一個 ASP.NET Core Web Application 應用程式來作範例,接著開啟 Nuget Package Mnage 輸入 AspNetCoreRateLimit 搜尋,安裝目前最新版的 AspNetCoreRateLimit 套件
      或是透過 Nuget Package Console 輸入下列指令
    Install-Package AspNetCoreRateLimit -Version 3.0.5
    安裝完畢之後到專案檔底下確認是否有安裝成功

    使用與設定 
    AspNetCoreRateLimit 可以根據 IP 或是客戶 ID 來作為限速依據,也可以指定 API 中某一個對外接口或是 Http Method 分別設定其限制,兩者皆是在中間件 ( Middleware) 判斷其請求率是否達到設定的上限,分別是  IpRateLimitMiddleware  與  ClientRateLimitMiddleware ,再來決定是否要讓此 Request 繼續下一步或是返回的動作,這裡以限制 IP 為範例,安裝完後接下來就是到 start.cs 中加入下列代碼
        public void ConfigureServices(IServiceCollection services)
        {
            // 將速限計數器資料儲存在 Memory 中
            services.AddMemoryCache();
    
            // 從 appsettings.json 讀取 IpRateLimiting 設定 
            services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
    
            // 從 appsettings.json 讀取 Ip Rule 設定
            services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
    
            // 注入 counter and IP Rules 
            services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
            services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
    
            // Add framework services.
            services.AddMvc();
    
            // the clientId/clientIp resolvers use it.
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
            // Rate Limit configuration 設定
            services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
    
            app.UseIpRateLimiting();
            app.UseMvc();
        }
    代碼中的 Code 都有加上註解跟說明,這裡就不在詳細說明,在上述代碼中可以看到 Policy 與 IP Rule 都是從 appSettings.json 設定檔中取得,因此下一步就是在設定檔中加上限流相關的設定資訊

    IpRateLimiting
    "IpRateLimiting": {
        "EnableEndpointRateLimiting": false,
        "StackBlockedRequests": false,
        "RealIpHeader": "X-Real-IP",
        "ClientIdHeader": "X-ClientId",
        "HttpStatusCode": 429,
        "IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
        "EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
        "ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
        "GeneralRules": [
          {
            "Endpoint": "*",
            "Period": "1s",
            "Limit": 2
          },
          {
            "Endpoint": "*",
            "Period": "15m",
            "Limit": 100
          },
          {
            "Endpoint": "*",
            "Period": "12h",
            "Limit": 1000
          },
          {
            "Endpoint": "*",
            "Period": "7d",
            "Limit": 10000
          }
        ]
      }
    IpRateLimiting 區塊設定 :  
  • EnableEndpointRateLimiting : 全部套用為 false,設定為 true 則 適用於每個 endPoint 。
  • StackBlockedRequests : false 代表拒絕的呼叫不會加入計數器中,如果希望被拒絕的請求也要包含在計數器中則要設定為 true。  
  • ClientIdHeader : 如果 Request 請求中的 Head 客戶 ID 與 ClientWhitelist 相同的話就不受限流設定影響。
  • GeneralRules : Period 設定可以包含天(d)、小時(h)、分鐘(m) 以及秒(s) 等設定值,舉例來說第一個區段設定限制為 1 秒限定 2 次,超過就會開始限流。

  • IpRateLimitPolicics
    在 appSettings.json 加入下列設定資訊
    "IpRateLimitPolicies": {
        "IpRules": [
          {
            "Ip": "84.247.85.224",
            "Rules": [
              {
                "Endpoint": "*",
                "Period": "1s",
                "Limit": 10
              },
              {
                "Endpoint": "*",
                "Period": "15m",
                "Limit": 200
              }
            ]
          },
          {
            "Ip": "192.168.3.22/25",
            "Rules": [
              {
                "Endpoint": "*",
                "Period": "1s",
                "Limit": 5
              },
              {
                "Endpoint": "*",
                "Period": "15m",
                "Limit": 150
              },
              {
                "Endpoint": "*",
                "Period": "12h",
                "Limit": 500
              }
            ]
          }
        ]
      }
    備註 : 這裡 IP 支援 IPv4 & IPv6 

    限流規則
    使用 Throttling 最重要的是了解限速的規則設定,才可以因應不同的情境調整需要的限速(流)設定,這裡直接來看一下官方說明文件提到的 Throttling 規則與 Rule,有 EndPoint Format、Period 與 Limit 等三種格式
    廢話不多說直接看範例更容易了解
    Sample 1 : 
    {
        "Endpoint": "*",
        "Period": "1s",
        "Limit": 2
    }
    目的 : 所有端點每秒鐘允許 2 次呼叫 
    使用 GET 方法發送 api / Values 一秒三個 Request,前 2 個 Request 會 pass,第 3 個 request 會無法使用,如果同時間使用 PUT 方法呼叫相同 api / values 則會 pass,因為 GET 與 PUT 分別加總不會混和計算。

     Sample 2 : 
    {
        "Endpoint": "*:/api/values",
        "Period": "15m",
        "Limit": 5
    }
    目的 : api / Values 用 HTTP 所有 Method 的設定為每 15 分鐘允許呼叫 5 次 

    Sample 3 : 
    {
        "Endpoint": "get:/api/values",
        "Period": "1h",
        "Limit": 5
    }
    目的 : api / Values 用 HTTP GET Method 的設定為每小時允許呼叫 5 次
    每小時呼叫 5 次,PASS 
    每小時呼叫 6 次,第六次則會正常取得正確資料

    當超過我們定義的次數限制時會顯示
    API calls quota exceeded! maximum admitted 2 per 10s.
    打開 Chrome 瀏覽器透過 Network 可以看到回傳的內容細節
    可以從開發者工具看到 Response 的資訊,在 Response Header 中 StatusCode 為 429,retry-after 為 9,如果不希望在 Response Header 中顯示 retry 次數,可以在 appsettings.json 設定檔中加上 DisableRateLimitHeaders 為 true 隱藏 retry 資訊,如果想要自訂 Response 時的訊息內容可以參考官網的說明 : 傳送門


    感想
    除了在 ASP.NET Core 有限流以外,作者也有提供 .NET Framework 版本的限流框架 WebApiThrottle,可以省下自己開發時間,也可以快速地將限流機制套用在需要的專案上,另外在本篇中提到的都是以 IP 為出發作為限制,在 AspNetCoreRateLimit 也有提供使用 Client 作為設定的機制,如有需要可以參考 ClientRateLimitMiddleware 相關說明,希望這篇介紹可以有幫助到有需要的朋友,謝謝

    參考
    AspNetCoreRateLimit
    Throttling your API in ASP.NET

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com