Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
In the following sections, I will do my best to explain my design process as well as how to use the Rate Limiter system.
Request Model
The
RequestModel
encapsulates all of the different data that can be used to identify and rate limit a given request.Rate Limiter
The
RateLimiter
is designed to do the following:RegisterRule
- Registers anIRateLimitRule
by adding it to the rate limiter's ruleset data storeIsRequestAllowedAsync
- goes through all configured rules for the resource that theRequsetModel
request is asking to access and checks each rule to ensure limits have not been exceeded.Rate Limiting Rules
You can add a new rate limiting rule through the following steps:
MyNewRule
BaseRateLimitRule
if you wish to utilize the semaphore logic withinIsRequestAllowedAsync
BaseRateLimitRule
, then your subclass must implement theProcessRuleAsync
methodBaseRateLimitRule
, then your subclass should implement theIsRequestAllowedAsync
directlyConstants.RateLimitRuleTypes.cs
RateLimitRuleFactory.CreateRule
methodRate Limit Data Store
In order to make it easier to switch from in-memory data store / cache to external data stores/caches, such as
Redis
, I chose to do the following:IRateLimitDataStore
interfaceIRateLimitDataStore
A new rate limit data store can be added to the system by doing the following:
MyNewRateLimitDataStore
IRateLimitDataStore
interfaceConstants.RateLimitDateStoreTypes
enum forMyNewRateLimitDataStore
RateLimitDataStoreFactory.CreateDataStore
methodData Store Key Generator
While developing different rate limit rules, I noticed that the rule logic could be shared across multiple instances. Rather than duplicate that logic, it seemed better to reuse the logic by creating a way to generate different rate limit data store keys based on the request and use those keys to access the data from the data store. To achieve this, I created the
DataStoreKeyGenerator
. One of the major benefits to utilizing the strategy pattern and adding aDataStoreKeyGenerator
turned out to be how easy it was to extend rule functionality and support different types of rules more easily.The
DataStoreKeyGenerator
generates a data store key string based on theDataStoreKeyType
and the givenRequestModel
request. For example, theDataStoreKeyTypes.RequestsPerUserPerResource
creates a key string by concatenating therequest.UserId
andrequest.RequestPath
together.To add a new
DataStoreKeyType
:Constants.DataStoreKeyTypes
Stores.DataStoreKeyGenerator
for the new enum type entrySample Usage
Instantiating the rate limiter:
Creating rules and registering them:
Checking if a request should be allowed: