using System.Collections.Generic;
private DateTime thisWindowStart;
private DateTime blockTime;
public Consumer(DateTime thisWindowStart) {
this.thisWindowStart = thisWindowStart;
public void IncrCounter() {
public bool PastLimit(int allowedCount) {
return this.count > allowedCount;
public bool IsWithinWindow(DateTime now, int period) {
return (now - this.thisWindowStart).TotalSeconds <= period;
public void ResetWindow(DateTime windowStart) {
this.thisWindowStart = windowStart;
public void SetBlockTime(DateTime blockTime) {
this.blockTime = blockTime;
public bool IsWithinBlockTime(DateTime now, int period) {
return (now - this.blockTime).TotalSeconds <= period;
public class TokenRateLimiter {
private int windowPeriod;
private int blockingPeriod;
private Dictionary<string, Consumer> consumers;
public TokenRateLimiter(int numRequest, int period, int blocking) {
this.numRequest = numRequest;
this.windowPeriod = period;
this.blockingPeriod = blocking;
this.consumers = new Dictionary<string, Consumer>();
public bool AllowRequest(string consumer) {
DateTime now = DateTime.Now;
Consumer thisConsumer = new Consumer(now);
if (this.consumers.ContainsKey(consumer)) {
thisConsumer = this.consumers[consumer];
this.consumers[consumer] = thisConsumer;
if (thisConsumer.IsWithinWindow(now, this.windowPeriod)) {
thisConsumer.IncrCounter();
if (thisConsumer.PastLimit(this.numRequest)) {
thisConsumer.SetBlockTime(now);
} else if (thisConsumer.IsWithinBlockTime(now, this.blockingPeriod)) {
thisConsumer.ResetWindow(now);
static void Main(string[] args)
var rateLimiter = new TokenRateLimiter(3, 1, 3);
Thread tselThread = new Thread(() =>
retry("tsel", 300, rateLimiter);
Thread xlThread = new Thread(() =>
retry("xl", 500, rateLimiter);
static void retry(string consumer, int interval, TokenRateLimiter rateLimiter)
bool allowed = rateLimiter.AllowRequest(consumer);
Console.WriteLine($"Request allowed for token '{consumer}'.");
Console.WriteLine($"Rate limit exceeded for token '{consumer}'. Try again later.");