Redis, ook wel genoemd Remote Dictionary Server, is een open-source in-memory database. Het is een gegevensgestructureerd opslagsysteem dat draait op het RAM-geheugen van een server, wat veel sneller is dan de snelste Solid State Drive (SSD). Hierdoor is Redis zeer responsief en uitermate geschikt voor rate limiting.
Rate limiting beperkt het aantal keren dat een gebruiker een bron van een server mag opvragen. Veel diensten gebruiken rate limits om misbruik van de dienst te voorkomen, zoals wanneer een gebruiker probeert een server te overbelasten met te veel belasting. Bijvoorbeeld, bij het gebruik van PHP om een openbare API (Application Programming Interface) voor je webapplicatie te ontwikkelen, zijn rate-beperkingen vereist. Dat komt omdat wanneer je een API openbaar maakt, je wilt beperken hoe vaak een individu een activiteit binnen een bepaalde tijd kan herhalen. Gebruikers die geen autoriteit over je systeem hebben, kunnen het immers tot stilstand brengen.
Rate limiting zorgt ervoor dat je applicatie soepel blijft functioneren door gebruikersverzoeken die een ingestelde limiet overschrijden te weigeren. Als je een groot aantal clients hebt, legt rate limiting een fair-use-beleid op waarmee elke gebruiker met hoge snelheid toegang krijgt tot je applicatie. Rate limiting kan je ook helpen geld te besparen op bandbreedte door congestie op je server te verminderen.
Door gebruikersactiviteit bij te houden in een database zoals MySQL, zou het mogelijk zijn om een rate-limiting-programma te maken. Omdat dergelijke gegevens echter van de schijf moeten worden gedownload en moeten worden geëvalueerd ten opzichte van de gedefinieerde limiet, is het eindresultaat mogelijk niet schaalbaar wanneer meerdere mensen contact opnemen met het systeem. Dit is niet alleen inefficiënt, maar relationele databasebeheersystemen zijn hier niet voor gebouwd.
Redis is een goede keuze voor het maken van een rate limiter omdat het werkt als een in-memory database en bewezen betrouwbaar is hiervoor. In deze handleiding leiden we je door de stappen voor het implementeren van PHP rate limiting met behulp van Redis op Ubuntu 20.04.
Laten we beginnen!
Vereisten
Om deze handleiding te volgen, heb je het volgende nodig:
-
De nieuwste versie van Ubuntu geïnstalleerd op je systeem.
-
Systeemgebruikers moeten sudo-privileges hebben.
-
Een LAMP-stack.
-
Stel dit in door de handleiding Hoe een LAMP-stack in te stellen te volgen.
-
-
Een Redis-server.
-
Volg de gids Hoe een Redis-server te installeren en te beveiligen om Redis op je systeem in te stellen.
-
Stap 1: Installeer de Redis-extensie voor PHP
Voordat we beginnen, updaten we de repositories om pakketconflicten te voorkomen:
|
1 |
sudo apt update |
Installeer vervolgens de php-redis-extensie, een pakket dat het mogelijk maakt om Redis te gebruiken in PHP-programma’s. Voer het volgende sudo-commando uit om php-redis:
|
1 |
sudo apt install -y php-redis |
Start daarna de Apache-server opnieuw op om de php-redis-bibliotheek te laden:
|
1 |
sudo systemctl restart apache2 |
De volgende stap is het bijwerken van de informatie in je software-index en het installeren van de Redis-bibliotheek voor PHP. Vervolgens maken we een PHP-bron die de toegang beperkt op basis van het IP-adres van een gebruiker.
Stap 2: Maak een PHP-webbron voor rate limiting
In deze stap maak je een demo.php-bestand in de root-directory van je webserver ( /var/www/html/). Dit bestand is openbaar toegankelijk en gebruikers kunnen de URL openen in hun favoriete webbrowser. Later zullen we het curl-commando gebruiken om de toegankelijkheid van de bron die we willen gebruiken te controleren. Gebruikers kunnen het voorbeeldbronbestand drie keer openen binnen een tijdsbestek van 15 seconden. Een poging die de maximale limiet overschrijdt, geeft een foutmelding.
De primaire functionaliteit van dit bestand is sterk afhankelijk van de Redis-server. De PHP-code in het bestand maakt een sleutel aan op de Redis-server op basis van het IP-adres van de gebruiker wanneer de gebruiker de bron voor het eerst bezoekt. De code zal proberen het IP-adres van de gebruiker te matchen met de sleutels die zijn opgeslagen in de Redis-server en de waarde met één verhogen als de sleutel bestaat wanneer de gebruiker terugkeert naar de bron. De PHP-code blijft controleren of de nieuwe waarde het maximale aantal heeft bereikt.
Na 15 seconden verloopt de Redis-sleutel, die is gebaseerd op het IP-adres van de gebruiker, en begint het bijhouden van de bezoeken van de gebruiker aan de webbron opnieuw. Open het /var/www/html/demo.php-bestand in de nano-teksteditor:
|
1 |
sudo nano /var/www/html/demo.php |
Vul vervolgens alle velden in om de Redis-klasse te initialiseren. Vergeet niet om de REDIS_PASSWORD op de juiste waarde in te stellen:
|
1 2 3 4 |
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->auth('REDIS_PASSWORD'); |
Redis->auth ondersteunt Redis-server authenticatie in platte tekst. Dit werkt goed als u lokaal werkt (via localhost), maar als u te maken hebt met een externe Redis-server, is SSL-authenticatie aanbevolen.
Stel vervolgens in hetzelfde bestand de volgende variabelen in op hun standaardwaarden:
|
1 2 3 4 5 |
. . . $max_calls_limit = 3; $time_period = 15; $total_user_calls = 0; |
Laten we deze verklaringen in detail bekijken:
-
$max_calls_limit: Een gebruiker heeft geen toegang tot de bron boven deze maximale limiet voor aanroepen.
-
$time_period: Dit wordt gebruikt als tijdsbestek en wordt geteld in seconden. Hier mag de gebruiker toegang krijgen tot de bron volgens de limieten die zijn ingesteld in de $max_calls_limit .
-
$total_user_calls: Sommeert het aantal keren dat een gebruiker toegang heeft gevraagd tot de aanroepen-limiet in een bepaalde periode.
Voeg vervolgens de volgende code toe om het IP-adres op te halen van de aanvrager die toegang vraagt tot de webpagina:
|
1 2 3 4 5 6 7 8 9 10 |
. . . if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $user_ip_address = $_SERVER['HTTP_CLIENT_IP']; } elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $user_ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $user_ip_address = $_SERVER['REMOTE_ADDR']; } |
Als demonstratie registreert deze code de acties van gebruikers op basis van hun IP-adressen. Als u een beveiligde bron op de server hebt die authenticatie vereist, kunt u de acties van gebruikers volgen met behulp van hun gebruikersnamen of toegangstokens.
In onze handleiding krijgt elke gebruiker die inlogt op uw systeem een unieke identificatie toegewezen (bijvoorbeeld een klant-ID, ontwikkelaars-ID, leveranciers-ID of zelfs een gebruikers-ID). Vergeet niet om deze ID's te gebruiken in plaats van het $user_ip adres als u onze handleiding volgt.
Hier is het IP-adres van de gebruiker voldoende om het concept te demonstreren. Voeg het volgende codeblok toe aan uw bestand zodra u het IP-adres van de gebruiker uit het voorgaande codefragment hebt verkregen:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
. . . if (!$redis->exists($user_ip_address)) { $redis->set($user_ip_address, 1); $redis->expire($user_ip_address, $time_period); $total_user_calls = 1; } else { $redis->INCR($user_ip_address); $total_user_calls = $redis->get($user_ip_address); if ($total_user_calls > $max_calls_limit) { echo "Gebruiker " . $user_ip_address . " limiet overschreden."; exit(); } } echo "Welkom " . $user_ip_address . " totaal aantal aanroepen " . $total_user_calls . " in " . $time_period . " seconden"; |
Hier is een overzicht van deze statements:
-
if...else: Het statement controleert of er een sleutel is gedefinieerd met het IP-adres op de Redis-server.
-
Als de sleutel niet is gevonden, if (!$redis->exists($user_ip_address)) {...}, kunt u de sleutel instellen en de waarde ervan op 1 definiëren met behulp van $redis->set($user_ip_address, 1).
-
-
$redis->expire($user_ip_address, $time_period): Zorgt ervoor dat de sleutel verloopt op een opgegeven tijdstip. Hier in deze handleiding hebben we deze ingesteld op 15 seconden.
-
Als het IP-adres van de gebruiker niet wordt gevonden in de Redis-sleutel, stel dan de variabele $total_user_calls waarde in als 1.
-
else {...}: Het statement-blok gebruikt het $redis->INCR($user_ip_address); commando om de waarde van de Redis-sleutel te verhogen met 1. Dit wordt toegepast op elk IP-adres dat aan de sleutel is gekoppeld.
-
Note: U kunt dit alleen bereiken wanneer de sleutel al is ingesteld in de Redis-server en wordt geteld als een herhaald verzoek.
-
-
$total_user_calls = $redis->get($user_ip_address): Dit statement haalt het totale aantal verzoeken op door de respectievelijke op IP-adres gebaseerde sleutel op de Redis-server te verifiëren.
-
if ($total_user_calls > $max_calls_limit) {... }..: Dit if statement wordt gebruikt om de overschreden limiet te verifiëren. Zo ja, dan stelt u de gebruiker op de hoogte met echo "Gebruiker" . $user_ip_address . " limiet overschreden.";.
Ten slotte stelt u de gebruiker op de hoogte van hun aantal bezoeken in een bepaalde periode met behulp van het echo "Welkom" . $user_ip_address . "totaal aantal aanroepen gedaan" . $total_user_calls . "in" . $time_period . "seconden"; statement.
Voeg daarna de volgende regels code toe aan uw /var/www/html/demo.php bestand:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->auth('REDIS_PASSWORD'); $max_calls_limit = 3; $time_period = 15; $total_user_calls = 0; if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $user_ip_address = $_SERVER['HTTP_CLIENT_IP']; }elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $user_ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $user_ip_address = $_SERVER['REMOTE_ADDR']; } if (!$redis->exists($user_ip_address)) { $redis->set($user_ip_address, 1); $redis->expire($user_ip_address, $time_period); $total_user_calls = 1; } else { $redis->INCR($user_ip_address); $total_user_calls = $redis->get($user_ip_address); if ($total_user_calls > $max_calls_limit) { echo "Gebruiker " . $user_ip_address . " limiet overschreden."; exit(); } } echo "Welkom " . $user_ip_address . " totaal aantal aanroepen gedaan " . $total_user_calls . " in " . $time_period . " seconden"; ?> |
Sla het /var/www/html/demo.php bestand op en sluit het nadat u klaar bent met het bewerken ervan. Op de demo.php-webpagina heeft u nu de logica gemaakt die nodig is om het aantal verzoeken van gebruikers te beperken. Laten we ons script testen in de volgende stap.
Stap 3: Voer de Redis Rate Limiting-test uit
U gebruikt het curl commando in deze stap om de webbron op te vragen die u hebt geschreven in Stap 2. Om het script grondig te testen, voert u een enkele opdracht uit die de bron vijf keer opvraagt. Dit kan worden bereikt door een placeholder URL-argument toe te voegen aan de demo.php einde van het bestand. Om de curl instructies vijf keer uit te voeren, gebruikt u de waarde ?[1-5] aan het einde van uw verzoek.
Voer de volgende curl opdracht hieronder uit:
|
1 |
curl -H "Accept: text/plain" -H "Content-Type: text/plain" -X GET http://localhost/demo.php?[1-5] |
Wanneer u de code uitvoert, zou u zoiets als dit moeten krijgen:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[1/5]: http://localhost/demo.php?1 --> <stdout> --_curl_--http://localhost/demo.php?1 Welkom 127.0.0.1 totaal aantal aanroepen gedaan 1 in 15 seconden [2/5]: http://localhost/demo.php?2 --> <stdout> --_curl_--http://localhost/demo.php?2 Welkom 127.0.0.1 totaal aantal aanroepen gedaan 2 in 15 seconden [3/5]: http://localhost/demo.php?3 --> <stdout> --_curl_--http://localhost/demo.php?3 Welkom 127.0.0.1 totaal aantal aanroepen gedaan 3 in 15 seconden [4/5]: http://localhost/demo.php?4 --> <stdout> --_curl_--http://localhost/demo.php?4 Gebruiker 127.0.0.1 limiet overschreden. [5/5]: http://localhost/demo.php?5 --> <stdout> --_curl_--http://localhost/demo.php?5 Gebruiker 127.0.0.1 limiet overschreden. |
De eerste drie verzoeken verliepen, zoals u kunt zien, zonder problemen. De vierde en vijfde query's werden echter door uw script in snelheid beperkt. Er is een goede kans dat de Redis-server de snelheid vertraagt waarmee mensen query's kunnen uitvoeren.
U hebt lage waarden opgegeven voor de twee variabelen die hieronder in deze handleiding worden vermeld:
|
1 2 3 4 |
... $max_calls_limit = 3; $time_period = 15; ... |
Wanneer u uw app in een productieomgeving bouwt, wilt u misschien nadenken over het gebruik van grotere getallen, afhankelijk van hoe vaak u denkt dat mensen deze zullen gebruiken.
Het is een goed idee om realtime statistieken te bekijken voordat u deze getallen aanpast. In dit voorbeeld, als uw serverlogboeken laten zien dat een gemiddelde gebruiker uw toepassing 1.000 keer per 60 seconden bezoekt, kunt u dat getal gebruiken als voorbeeld van de mate van throttling die u moet toepassen.
Conclusie
In deze handleiding hebt u geleerd hoe u een Redis-server met PHP op Ubuntu 20.04 kunt gebruiken. Hoewel deze post laat zien hoe rate limiting werkt met Redis, kunt u deze aanpassen aan de behoeften van uw webapplicatie. We raden u aan om praktijkvoorbeelden te bekijken, zoals de maximale verzoeklimiet van Twitter, Google’s Custom Search JSON API, en andere soortgelijke documentatie om uw kennis over rate limiting te vergroten en zelf te experimenteren met verschillende tijdslimieten.
Daarnaast zijn er nog veel meer leermaterialen over Redis en PHP die u kunt raadplegen op onze blogs:
- Een PHP-applicatie implementeren op een Kubernetes-cluster met Ubuntu 18.04
- PHPmyadmin installeren en beveiligen op Ubuntu 20.04
- De LEMP-stack (Linux Nginx MySQL PHP) installeren op Ubuntu 20.04
Veel computerplezier!
Reacties
Nog geen reacties. Wees de eerste.