Created
August 1, 2022 10:13
-
-
Save Brom95/c4736aae78f870785ed19c098d3f86ca to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Extensions.Logging; | |
namespace RateLimiterCore; | |
public class RateLimiter<T> : IRateLimiter<T>, IDisposable | |
{ | |
private int count = 0; | |
private Timer windowTimer; | |
private readonly TimeSpan windowSize; | |
private static readonly RateLimiterOptions DefaultOptions = new(); | |
private uint maxConcurrentRequests; | |
private readonly CancellationTokenSource cancellationTokenSource; | |
private readonly ILogger<RateLimiter<T>> logger; | |
public RateLimiter(IRateLimiterOptions? options = null, ILogger<RateLimiter<T>> logger = null) | |
{ | |
options ??= DefaultOptions; | |
windowSize = options.WindowSize; | |
maxConcurrentRequests = options.ConcurrentRequests; | |
cancellationTokenSource = new CancellationTokenSource(); | |
this.logger = logger; | |
} | |
public async Task<Result<T>> Invoke(Func<Task<T>> action, CancellationToken cancellationToken) | |
{ | |
windowTimer ??= new Timer((_) => Interlocked.Exchange(ref count, 0), null, windowSize, windowSize); | |
int initialValue, computedValue; | |
do | |
{ | |
initialValue = count; | |
if (initialValue < maxConcurrentRequests && !cancellationTokenSource.IsCancellationRequested) | |
{ | |
computedValue = initialValue + 1; | |
} | |
else | |
{ | |
// RateLimiter<T>.Log($"Fail"); | |
logger?.LogDebug("Fail"); | |
return Result<T>.Fail(); | |
} | |
} | |
while (initialValue != Interlocked.CompareExchange(ref count, computedValue, initialValue)); | |
var task = Task.Run(action, cancellationToken); | |
logger?.LogInformation("Success {task} Count: {count}", task.Id, count); | |
return Result<T>.Success(await task); | |
} | |
public void Dispose() | |
{ | |
maxConcurrentRequests = 0; | |
cancellationTokenSource.Cancel(); | |
windowTimer.Dispose(); | |
} | |
} | |
public interface IRateLimiterOptions | |
{ | |
uint ConcurrentRequests { get; set; } | |
TimeSpan WindowSize { get; set; } | |
} | |
public class RateLimiterOptions : IRateLimiterOptions | |
{ | |
public uint ConcurrentRequests { get; set; } = 5; | |
public TimeSpan WindowSize { get; set; } = TimeSpan.FromSeconds(5); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment