MiniAPI(二十一)：RateLimit

レートリミットとは、ネットワークの基盤設傼で設定して実装することもできますし、ゲートウェイでレートリミットを行うことも可能です。しかし、サービス自体のレートリミットも欠かせません。なぜなら、複数のレプリカがある場合、一つのレプリカが障害を起こすと、他のレプリカに対するトラフィックが増加し、それが耐えられるリクエスト量を超えると、サービスが連続してクラッシュする原因となるためです。したがって、個々のサービス自体でもレートリミットを実装することが望ましいです。

ASP.NET Coreプロジェクトでは、AspNetCoreRateLimitを導入することでレートリミット処理が可能です。
以下の方法でNuGetパッケージを導入できます。

```
Install-Package AspNetCoreRateLimit
```

クライアントレートリミットの設定:

```csharp
using AspNetCoreRateLimit;
var builder = WebApplication.CreateBuilder(args);

// メモリキャッシュサービスを注入
builder.Services.AddMemoryCache();

// ClientRateLimiting設定ファイルを読み込み
builder.Services.Configure<ClientRateLimitOptions>(builder.Configuration.GetSection("ClientRateLimiting"));

// ClientRateLimitPolicies設定ファイルを読み込み
builder.Services.Configure<ClientRateLimitPolicies>(builder.Configuration.GetSection("ClientRateLimitPolicies"));

// レートリミットメモリキャッシュサービスを注入
builder.Services.AddInMemoryRateLimiting();

// レートリミット設定ファイルサービスを注入
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();

var app = builder.Build();

// ClientRateLimitPoliciesを有効化
var clientPolicyStore = app.Services.GetRequiredService<IClientPolicyStore>();
await clientPolicyStore.SeedAsync();

// クライアントレートリミットミドルウェアを使用
app.UseClientRateLimiting();

app.MapGet("/test00", () => "get test00 ok");
app.MapGet("/test01", () => "get test01 ok");
app.MapGet("/test02", () => "get test02 ok");
app.MapPost("/test02", () => "post test02 ok");

app.Run();
```

appsetings.json

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ClientRateLimiting": {
    "EnableEndpointRateLimiting": false,
    "StackBlockedRequests": false,
    "ClientIdHeader": "X-ClientId",
    "HttpStatusCode": 429,
    "EndpointWhitelist": [ "get:/test00", "*:/test01" ],
    "ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
    "GeneralRules": [
      {
        "Endpoint": "*",
        "Period": "5s",
        "Limit": 2
      },
      {
        "Endpoint": "*",
        "Period": "10s",
        "Limit": 3
      }
    ]  
  },   
  "ClientRateLimitPolicies": {
    "ClientRules": [
      {
        "ClientId": "client-id-1",
        "Rules": [
          {
            "Endpoint": "*",
            "Period": "5s",
            "Limit": 1
          },
          {
            "Endpoint": "*",
            "Period": "15m",
            "Limit": 200
          }
        ]
      },
      {
        "ClientId": "client-id-2",
        "Rules": [
          {
            "Endpoint": "*",
            "Period": "1s",
            "Limit": 5
          },
          {
            "Endpoint": "*",
            "Period": "15m",
            "Limit": 150
          },
          {
            "Endpoint": "*",
            "Period": "12h",
            "Limit": 500
          }
        ]
      }
    ]
  }
}
```

設定説明:
- `EnableEndpointRateLimiting`がfalseの場合、全てのリクエストの総数が閾値を超えた時にレートリミットされます。trueの場合は、各リクエストが閾値を超えた時にレートリミットされます。
- `StackBlockedRequests`がfalseの場合、前5秒間に2回成功し1回失敗した場合、6秒後にはさらに1回成功することができます。trueの場合、6秒後のリクエストは成功しません。
- `ClientIdHeader`はレートリミットの黒白リストを処理するために、ヘッダーキー`X-ClientId`を指定します。
- `ClientWhitelist`は`dev-id-1`、`dev-id-2`で、ヘッダー内の`X-ClientId`がこれらの値であれば、通過させます。
- `EndpointWhitelist`はレートリミットに含まれないエンドポイントです。
- `HttpStatusCode`はレートリミット後に返されるステータスコードです。
- `GeneralRules`は一般的なレートリミット規則です。
- `ClientRateLimitPolicies`設定は、異なる`X-ClientId`に対して異なるレートリミット設定を行うためのもので、クライアントIDのグレーリストを意味します。

また、ClientIDによるレートリミットだけではなく、クライアントのリクエストIPに対してもレートリミットを行うことができ、設定は同様です。

IPレートリミットの設定:

```csharp
using AspNetCoreRateLimit;
var builder = WebApplication.CreateBuilder(args);

// メモリキャッシュサービスを注入
builder.Services.AddMemoryCache();

// IPRateLimiting設定ファイルを読み込み
builder.Services.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("IpRateLimiting"));

// IPRateLimitPolicies設定ファイルを読み込み
builder.Services.Configure<IpRateLimitPolicies>(builder.Configuration.GetSection("IpRateLimitPolicies"));

// レートリミットメモリキャッシュサービスを注入
builder.Services.AddInMemoryRateLimiting();

// レートリミット設定ファイルサービスを注入
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();

var app = builder.Build();

// IPRateLimitPoliciesを有効化
var ipPolicyStore = app.Services.GetRequiredService<IIpPolicyStore>();
await ipPolicyStore.SeedAsync();

// IPレートリミットミドルウェアを使用
app.UseIpRateLimiting();

app.MapGet("/test00", () => "get test00 ok");
app.MapGet("/test01", () => "get test01 ok");
app.MapGet("/test02", () => "get test02 ok");
app.MapPost("/test02", () => "post test02 ok");
app.MapGet("/test03", () => "get test01 ok");

app.Run();
```

appsettings.json

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "IpRateLimiting": {
    "EnableEndpointRateLimiting": false,
    "StackBlockedRequests": false,
    "RealIpHeader": "X-Real-IP",
    "IpWhitelist": [ "127.0.0.1"],
    "ClientIdHeader": "X-ClientId",
    "HttpStatusCode": 429,
    "EndpointWhitelist": [ "get:/test00", "*:/test01" ],
    "ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
    "GeneralRules": [
      {
        "Endpoint": "*",
        "Period": "5s",
        "Limit": 2
      },
      {
        "Endpoint": "*",
        "Period": "10s",
        "Limit": 3
      }
    ]  
  },
  "IpRateLimitPolicies": {
    "IpRules": [
      {
        "Ip": "127.0.0.2",
        "Rules": [
          {
            "Endpoint": "*",
            "Period": "4s",
            "Limit": 1
          },
          {
            "Endpoint": "*",
            "Period": "15m",
            "Limit": 200
          }
        ]
      }
    ]  
  }
}
```
(Translated by GPT)

元のリンク：https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247484966&idx=1&sn=b02cad0b0ce96ce33d4a04bf26f7a77d&chksm=9f00590ca877d01ab1898570f8557cd3356d8c7c562eb8dab4d51083738a40d1d0550e344d21&token=541757603&lang=zh_CN#rd