【ASP.NET Core Web】レートリミットを実装する
2024-4-22 | ASP.NET Core Web, C#
ASP .NET Core Webでレートリミットを実装したい!
概要
今回の記事では、ASP .NET Core Webでミドルウェアを使ってレートリミットを実装する手順を掲載する。
レートリミットとは、例えば一定時間内でAPIサーバーへのリクエスト回数を制限して、サーバーへの負荷が大きくなりすぎないようにするやり方のひとつ。
仕様書
環境
- .NET 8
手順書
何に対してどうレートリミットをかけるかのか、いろいろなやり方があるんだけども、この記事ではクライアントIPのアクセスに対して、Sliding Log方式でレートリミットを実装した。
レートリミットを実装したクラスRateLimiterMiddleware
は下記のような感じになった。
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class RateLimiterMiddleware
{
private readonly RequestDelegate next;
private readonly int maxRequests;
private readonly TimeSpan timeSpan;
private static ConcurrentDictionary<string, ConcurrentQueue<DateTime>> _requestLogs = new ConcurrentDictionary<string, ConcurrentQueue<DateTime>>();
public RateLimiterMiddleware(RequestDelegate next, int maxRequests, TimeSpan timeSpan)
{
this.next = next;
this.maxRequests = maxRequests;
this.timeSpan = timeSpan;
}
public async Task InvokeAsync(HttpContext context)
{
var clientIp = context.Connection.RemoteIpAddress?.ToString();
if (clientIp == null)
{
await next(context);
return;
}
var requestLog = _requestLogs.GetOrAdd(clientIp, new ConcurrentQueue<DateTime>());
var now = DateTime.UtcNow;
lock (requestLog)
{
while (requestLog.TryPeek(out var timestamp) && (now - timestamp) > timeSpan)
{
requestLog.TryDequeue(out _);
}
requestLog.Enqueue(now);
if (requestLog.Count > maxRequests)
{
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context.Response.WriteAsync("Rate limit exceeded! Please try again later!!");
return;
}
}
await next(context);
}
}
Program.cs
にRateLimiterMiddleware
の設定を追加する。
...
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
...
app.UseMiddleware<RateLimiterMiddleware>(10, TimeSpan.FromMinutes(5));
...
app.Run();
}
}
第1引数は一定時間内にリクエストできる回数。
第2引数はどのくらい遡ってリクエストの回数をカウントするかの時間。
例では5分間に10回を超えるとステータスコード429
を返す感じ。
特定のアドレスにレートリミットをかけたい場合の例。
...
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
...
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), builder =>
{
builder.UseMiddleware<RateLimiterMiddleware>(10, TimeSpan.FromMinutes(5));
});
...
app.Run();
}
}
example.com/api
以下のURLへのリクエストにレートリミットが掛かるようになる。
まとめ(感想文)
サーバーの負荷をコントールしたい時や従量課金のクラウドを使うとコストを抑えれる時に使えるかもね!