A php client for the lyft/ratelimit gRPC service.
Proto Version: v1.3.0.
This section describes the typical usage of the client within a php application to implement request blocking upon reaching the configured rate limits.
An instance of the ratelimit gRPC service should be running on the local workstation. A
docker-compose.yml
file is provided by the upstream project to stand up the ratelimit
service
and a redis instance.
An example client implementation is provided in the examples
directory.
The code snippets in this section are representative when using the following service configuration:
domain: api
descriptors:
- key: remote_address
rate_limit:
unit: minute
requests_per_unit: 60
- key: client_id
rate_limit:
unit: minute
requests_per_unit: 600
Ensure that the grpc php extension is installed and enabled per the gRPC docs:
pecl install grpc
Install the library with composer:
composer require traxo/ratelimit-php-client
Instantiate a client:
<?php
$client = new Traxo\RateLimit\Client([
'address' => 'localhost:8081'
]);
Build a request object with a complex argument:
$req = new Pb\Lyft\Ratelimit\RateLimitRequest([
'domain' => 'api',
'descriptors' => [
new Pb\Lyft\Ratelimit\RateLimitDescriptor([
'entries' => [
new Pb\Lyft\Ratelimit\RateLimitDescriptor\Entry([
'key' => 'remote_address',
'value' => '10.0.2.15',
]),
]
])
],
'hits_addend' => 2,
]);
Or use the provided setters to build a request object:
$req->setDomain('api');
$req->setDescriptors([
new Pb\Lyft\Ratelimit\RateLimitDescriptor([
'entries' => [
new Pb\Lyft\Ratelimit\RateLimitDescriptor\Entry([
'key' => 'remote_address',
'value' => '10.0.2.15',
]),
// Additional variants that can be tried:
// new Pb\Lyft\Ratelimit\RateLimitDescriptor\Entry([
// 'key' => 'client_id',
// 'value' => 'abc1234',
// ]),
]
])
]);
Call the ratelimit network service and wait for the response:
try {
list($response, $status) = $client->ShouldRateLimit($req)->wait();
} catch (Exception $e) {
var_dump($e->getMessage());
exit;
}
The gRPC request status should be checked:
if (empty($status) || ($status->code !== 0)) {
echo 'Request failed: ' . $status->details . PHP_EOL;
exit;
}
Most implementations will block additional requests when the overall code is OVER_LIMIT
:
if ($response->getOverallCode() === 2) {
// block the request here
}
This client can generate conventional rate limiting headers from the service response:
$headers = $client->getHeaders($response);
foreach ($headers as $header) {
// handle $header here
}
where $headers
is an array of header objects for each matched descriptor in the domain configuration:
[
{
"X-RateLimit-Limit" => 60,
"X-RateLimit-Remaining" => 59
}
]
In the case where multiple descriptors are matched, $headers
is ranked in order of least remaining
limit on a percentage basis (i.e. (Remaining / Limit) * 100 ). Most implementers will elect to use
the first header object and ignore the others.
Note that the X-RateLimit-Reset
header is not yet supported. Monitor this
pull request for updates.
The header prefix (i.e. X-RateLimit
) is configurable.
This client implements the protocol buffer file (i.e. ratelimit.proto
) from the ratelimit project
repository. The gRPC project provides code generation across many programming languages. The
generated php classes are used by this client.
Prior to generating the client code, the local workstation must be properly configured.
This section generally follows the PHP Quickstart from the gRPC project website.
- Install the gRPC php extension:
sudo pecl install grpc
or
sudo yum install --enablerepo=epel,remi,remi-php73 php-pecl-grpc
- Add the module file
/etc/php.d/40-grpc.ini
if not already installed:
; Enable "General RPC framework" extension module
extension = grpc.so
- Install the Protobuf runtime library:
sudo pecl install protobuf
or
sudo yum install --enablerepo=epel,remi,remi-php73 php-pecl-protobuf
- Add the module file
/etc/php.d/40-protobuf.ini
if not already installed:
; Enable protobuf extension module
extension = protobuf.so
- Install the protobuf compiler:
cd /tmp
sudo yum install -y libatomic
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.7.0/protoc-3.7.0-linux-x86_64.zip
unzip protoc-3.7.0-linux-x86_64.zip
sudo cp bin/protoc /usr/local/bin/
sudo cp -Rf include/google /usr/local/include/
sudo chmod a+rx /usr/local/bin/protoc
cd -
This section describes how to generate or re-generate the generated php classes used by this client.
-
Copy the
ratelimit.proto
file from lyft/ratelimit service project. -
Checkout the gRPC repository next to this one:
git clone -b v1.19.0 https://github.com/grpc/grpc
- Run the
proto-gen.sh
script from the root of this client repository. The script uses relative paths to locate thegrpc
repository on the local filesystem:
sh proto-gen.sh
-
Implement
DNS SRV
and/or consul service support for service discovery. This will allow the address for each request to be resolved at runtime and avoid the requirement for a load balancer in front of the ratelimit service simply to normalize the address and port. -
The
X-RateLimit-Reset
header is not yet supported. Monitor this pull request for updates. -
Decide how to generate headers when no descriptors match the conditions.
-
Finish development of
Request
andResponse
classes that are tailored to typical use cases in php, can handle simple descriptor entries in a more compact fashion.
While attempting to extend \Pb\Lyft\Ratelimit\RateLimitRequest
in a Request
class, the following
error was encountered:
PHP Notice: Traxo\RateLimit\Request is not found in descriptor pool. in /vagrant/ratelimit-php-client/vendor/google/protobuf/php/src/Google/Protobuf/Internal/Message.php on line 96
PHP Fatal error: Uncaught Error: Call to a member function getField() on null in /vagrant/ratelimit-php-client/vendor/google/protobuf/php/src/Google/Protobuf/Internal/Message.php:98
This could be a limitation of protocol buffers and the lack of class inheritance properties. If so,
a Request
class may need to instead marshall the requests through the generated request class and
handle the response independent of the generated response class.
MIT