Stars: 126
Forks: 15
Pull Requests: 123
Issues: 8
Watchers: 7
Last Updated: 2021-09-21 17:48:35
A Guzzle middleware that can throttle requests according to (multiple) defined rules. It is also possible to define a caching strategy, e.g. get the response from cache when the rate limit is exceeded or always get a cached value to spare your rate limits. Using wildcards in host names is also supported.
License: MIT License
Languages: PHP
A Guzzle middleware that throttles requests according to (multiple) defined rules.
It is also possible to define a caching strategy. For example, the response can be read from a cache when exceeding rate limits. The cached value can also be preferred to spare your rate limits (force-cache
).
Using wildcards in hostnames is also supported.
Via Composer
composer require hamburgscleanest/guzzle-advanced-throttle
Let's say you wanted to implement the following rules:
20 requests every 1 seconds
100 requests every 2 minutes
hamburgscleanest\GuzzleAdvancedThrottle\RequestLimitRuleset
:$rules = new RequestLimitRuleset([
'https://www.google.com' => [
[
'max_requests' => 20,
'request_interval' => 1
],
[
'max_requests' => 100,
'request_interval' => 120
]
]
]);
$stack = new HandlerStack();
$stack->setHandler(new CurlHandler());
hamburgscleanest\GuzzleAdvancedThrottle\Middleware\ThrottleMiddleware
to the stack.It should always be the first middleware on the stack.
$throttle = new ThrottleMiddleware($rules);
// Invoke the middleware
$stack->push($throttle());
// OR: alternatively call the handle method directly
$stack->push($throttle->handle());
$client = new Client(['base_uri' => 'https://www.google.com', 'handler' => $stack]);
Either the base_uri
has to be the same as the defined host in the rules array or you have to request absolute URLs for the middleware to have an effect.
// relative
$response = $client->get('test');
// absolute
$response = $client->get('https://www.google.com/test');
Responses with an error status code 4xx
or 5xx
are not cached (even with force-cache
enabled)!
Note: Currently, also redirect responses (3xx
) are not cached.
array
(default)This adapter works out of the box. However, it does not persist
anything. This one only works within the same scope. It's set as a default because it doesn't need extra configuration.
The recommended adapter is the laravel
one.
laravel
(Illuminate/Cache) - recommendedYou need to provide a config (Illuminate\Config\Repository
) for this adapter.
custom
(Implements hamburgscleanest\GuzzleAdvancedThrottle\Cache\Interfaces\StorageInterface
)When you create a new implementation, pass the class name to the RequestLimitRuleset::create
method.
You'll also need to implement any sort of configuration parsing your instance needs.
Please see LaravelAdapter
for an example.
$rules = new RequestLimitRuleset(
[ ... ],
'force-cache', // caching strategy
MyCustomAdapter::class // storage adapter
);
$throttle = new ThrottleMiddleware($rules);
// Invoke the middleware
$stack->push($throttle());
These values can be set for every adapter.
'cache' => [
'ttl' => 900, // How long should responses be cached for (in seconds)?
'allow_empty' => true // When this is set to false, empty responses won't be cached.
]
'cache' => [
'driver' => 'file',
'options' => [
'path' => './cache'
],
...
]
'cache' => [
'driver' => 'redis',
'options' => [
'database' => [
'cluster' => false,
'default' => [
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
]
],
...
]
'cache' => [
'driver' => 'memcached',
'options' => [
'servers' => [
[
'host' => '127.0.0.1',
'port' => 11211,
'weight' => 100,
],
]
],
...
]
$rules = new RequestLimitRuleset(
[ ... ],
'cache', // caching strategy
'laravel', // storage adapter
new Repository(require '../config/laravel-guzzle-limiter.php') // config repository
);
The same adapter will be used to store the internal request timers.
$rules = new RequestLimitRuleset(
[ ... ],
'cache', // caching strategy
'array' // storage adapter
);
no-cache
Just throttle the requests. The responses are not cached. Exceeding the rate limits results in a 429 - Too Many Requests
exception.
$rules = new RequestLimitRuleset(
[ ... ],
'no-cache', // caching strategy
'array' // storage adapter
);
cache
The middleware tries to fall back to a cached value when the rate limits are exceeded before throwing a 429 - Too Many Requests
exception.
$rules = new RequestLimitRuleset(
[ ... ],
'cache', // caching strategy
'array' // storage adapter
);
force-cache
Always use cached responses when available to spare your rate limits.
As long as there is a response in the cache for the current request, it returns the cached response.
It will only actually send the request when no response is in the cache.
Otherwise, it throws a 429 - Too Many Requests
exception.
You might want to disable the caching of empty responses with this option (see General Driver Settings).
$rules = new RequestLimitRuleset(
[ ... ],
'force-cache', // caching strategy
'array' // storage adapter
);
The custom caching strategy must implement the CacheStrategy
interface. It is advised to use the Cacheable
abstraction to implement base functionality. For reference implementations, please check ForceCache
and Cache
.
To use the new caching strategy, you'll need to pass the fully qualified class name to RequestLimitRuleset
.
$rules = new RequestLimitRuleset([ ... ],
MyCustomCacheStrategy::class,
'array',
new Repository(...));
$throttle = new ThrottleMiddleware($rules);
...
If you want to define the same rules for multiple different hosts, you can use wildcards. A possible use case can be subdomains:
$rules = new RequestLimitRuleset([
'https://www.{subdomain}.mysite.com' => [
[
'max_requests' => 50,
'request_interval' => 2
]
]
]);
This host
matches https://www.en.mysite.com
, https://www.de.mysite.com
, https://www.fr.mysite.com
, etc.
Please see CHANGELOG for more information on what has changed recently.
composer test
Please see CONTRIBUTING and CODE_OF_CONDUCT for details.
If you discover any security-related issues, please email [email protected] instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.