Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- public abstract class Disposable : IDisposable
- {
- private bool _disposed;
- public void Dispose()
- {
- if (_disposed)
- {
- return;
- }
- Dispose(_disposed = true);
- }
- protected void ThrowIfDisposed(IDisposable disposable)
- {
- if (_disposed)
- {
- throw new ObjectDisposedException(disposable.GetType().Name);
- }
- }
- protected abstract void Dispose(bool disposing);
- }
- public interface IRateLimiter : IDisposable
- {
- TimeSpan Interval { get; set; }
- int Limit { get; set; }
- Task ThrottleAsync(int weight = 1, CancellationToken token = default);
- }
- public class RateLimiter : Disposable, IRateLimiter
- {
- private readonly LinkedList<RateLimitRequest> _requests = new LinkedList<RateLimitRequest>();
- private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
- public RateLimiter(TimeSpan interval, int limit)
- {
- Interval = interval;
- Limit = limit;
- }
- public TimeSpan Interval { get; set; }
- public int Limit { get; set; }
- public async Task ThrottleAsync(int weight = 1, CancellationToken token = default)
- {
- if (weight < 1)
- {
- return;
- }
- ThrowIfDisposed(this);
- var count = 0;
- var current = DateTime.UtcNow;
- var last = DateTime.MinValue;
- var target = current - Interval;
- await _semaphore.WaitAsync(token);
- try
- {
- var node = _requests.First;
- while (node != null)
- {
- var timestamp = node.Value.Timestamp;
- var next = node.Next;
- if (timestamp > target)
- {
- if (count + weight <= Limit)
- {
- last = timestamp;
- count += node.Value.Weight;
- }
- }
- else
- {
- _requests.Remove(node);
- }
- node = next;
- }
- if (count + weight <= Limit)
- {
- _requests.AddFirst(new RateLimitRequest
- {
- Weight = weight,
- Timestamp = DateTime.UtcNow
- });
- return;
- }
- var delay = last.Add(Interval).Subtract(current);
- await Task.Delay(delay, token);
- _requests.AddFirst(new RateLimitRequest
- {
- Weight = weight,
- Timestamp = DateTime.UtcNow
- });
- }
- catch (Exception)
- {
- /* Ignore */
- }
- finally
- {
- _semaphore.Release();
- }
- }
- protected override void Dispose(bool disposing)
- {
- if (!disposing)
- {
- return;
- }
- _semaphore?.Dispose();
- }
- private class RateLimitRequest
- {
- public DateTime Timestamp { get; set; }
- public int Weight { get; set; }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement