Redis, zwany również Remote Dictionary Server, to otwartoźródłowa baza danych w pamięci. Jest to system przechowywania struktur danych działający w pamięci RAM serwera, który jest znacznie szybszy niż najszybszy dysk półprzewodnikowy (SSD). W rezultacie Redis jest bardzo responsywny i doskonale nadaje się do ograniczania liczby żądań.
Ograniczanie liczby żądań ogranicza liczbę prób, w których użytkownik może zażądać zasobu z serwera. Wiele usług korzysta z limitów żądań, aby zapobiec nadużyciom, na przykład gdy użytkownik próbuje przeciążyć serwer zbyt dużym obciążeniem. Na przykład podczas korzystania z PHP do stworzenia publicznego API (Application Programming Interface) dla swojej aplikacji internetowej, ograniczenia liczby żądań są niezbędne. Dzieje się tak, ponieważ udostępniając API publicznie, chcesz ograniczyć liczbę powtórzeń danej czynności przez jedną osobę w określonym czasie. Użytkownicy, którzy nie mają uprawnień do Twojego systemu, mogliby doprowadzić do jego paraliżu.
Ograniczanie liczby żądań pozwala na płynne działanie aplikacji poprzez odrzucanie żądań użytkowników, które przekraczają ustalony limit. Jeśli masz dużą liczbę klientów, ograniczenie to wprowadza zasadę uczciwego korzystania (fair-use policy), która pozwala każdemu użytkownikowi na szybki dostęp do aplikacji. Ograniczanie liczby żądań może również pomóc zaoszczędzić pieniądze na przepustowości poprzez zmniejszenie obciążenia serwera.
Śledząc aktywność użytkowników w bazie danych takiej jak MySQL, możliwe byłoby stworzenie programu do ograniczania liczby żądań. Ponieważ jednak takie dane musiałyby być pobierane z dysku i porównywane ze zdefiniowanym limitem, ostateczne rozwiązanie mogłoby nie być skalowalne, gdy z systemem łączy się wiele osób jednocześnie. Jest to nie tylko nieefektywne, ale relacyjne systemy zarządzania bazami danych nie zostały do tego stworzone.
Redis jest dobrym wyborem do stworzenia mechanizmu ograniczania liczby żądań, ponieważ działa jako baza danych w pamięci i został sprawdzony pod kątem niezawodności w tym zastosowaniu. W tym samouczku przeprowadzimy Cię przez kroki wdrażania ograniczania liczby żądań w PHP przy użyciu Redis na Ubuntu 20.04.
Zaczynajmy!
Wymagania wstępne
Aby móc postępować zgodnie z tym samouczkiem, będziesz potrzebować:
-
Najnowszej wersji systemu Ubuntu zainstalowanej w swoim systemie.
-
Użytkownicy systemu muszą posiadać uprawnienia sudo.
-
Stosu LAMP.
-
Skonfiguruj go, postępując zgodnie z samouczkiem Jak skonfigurować stos LAMP.
-
-
Serwera Redis.
-
Postępuj zgodnie z poradnikiem Jak zainstalować i zabezpieczyć serwer Redis, aby skonfigurować Redis w swoim systemie.
-
Krok 1: Instalacja rozszerzenia Redis dla PHP
Zanim zaczniemy, zaktualizujmy repozytoria, aby uniknąć konfliktów pakietów:
|
1 |
sudo apt update |
Następnie zainstaluj rozszerzenie php-redis, czyli pakiet umożliwiający korzystanie z Redis w programach PHP. Uruchom następujące polecenie sudo, aby zainstalować php-redis:
|
1 |
sudo apt install -y php-redis |
Następnie zrestartuj serwer Apache, aby załadować bibliotekę php-redis:
|
1 |
sudo systemctl restart apache2 |
Kolejnym krokiem jest aktualizacja informacji w indeksie oprogramowania i instalacja biblioteki Redis dla PHP. Następnie utworzymy zasób PHP, który ogranicza dostęp na podstawie adresu IP użytkownika.
Krok 2: Tworzenie zasobu sieciowego PHP do ograniczania liczby żądań
W tym kroku utworzysz plik demo.php w katalogu głównym serwera WWW ( /var/www/html/). Plik ten będzie publicznie dostępny, a użytkownicy będą mogli otworzyć ten adres URL w swojej preferowanej przeglądarce internetowej. Później użyjemy polecenia curl, aby zweryfikować dostępność zasobu, z którego chcemy skorzystać. Użytkownicy mogą uzyskać dostęp do przykładowego pliku zasobu trzy razy w ciągu 15 sekund. Próba przekraczająca maksymalny limit spowoduje wyświetlenie komunikatu o błędzie.
Główna funkcjonalność tego pliku jest silnie zależna od serwera Redis. Kod PHP w pliku tworzy klucz na serwerze Redis na podstawie adresu IP użytkownika, gdy ten uzyskuje dostęp do zasobu po raz pierwszy. Kod spróbuje dopasować adres IP użytkownika do kluczy zapisanych na serwerze Redis i zwiększy wartość o jeden, jeśli klucz istnieje, gdy użytkownik powróci do zasobu. Kod PHP będzie stale sprawdzał, czy nowa wartość osiągnęła maksymalny limit.
Po 15 sekundach klucz Redis, który jest oparty na adresie IP użytkownika, wygaśnie, a śledzenie wizyt użytkownika w zasobie internetowym rozpocznie się od nowa. Otwórz plik /var/www/html/demo.php plik w edytorze tekstu nano:
|
1 |
sudo nano /var/www/html/demo.php |
Następnie wypełnij wszystkie pola, aby zainicjować klasę Redis. Nie zapomnij ustawić REDIS_PASSWORD na poprawną wartość:
|
1 2 3 4 |
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->auth('REDIS_PASSWORD'); |
Redis->auth obsługuje uwierzytelnianie otwartym tekstem serwera Redis. Działa to dobrze, jeśli pracujesz lokalnie (przez localhost), ale jeśli masz do czynienia ze zdalnym serwerem Redis, uwierzytelnianie SSL jest zalecane.
Następnie w tym samym pliku ustaw następujące zmienne na ich wartości domyślne:
|
1 2 3 4 5 |
. . . $max_calls_limit = 3; $time_period = 15; $total_user_calls = 0; |
Przyjrzyjmy się szczegółowo tym instrukcjom:
-
$max_calls_limit: Użytkownik nie może uzyskać dostępu do zasobu powyżej tego maksymalnego limitu wywołań.
-
$time_period: Jest to używane jako ramy czasowe i jest liczone w sekundach. Tutaj użytkownik ma zezwolenie na dostęp do zasobu zgodnie z limitami ustawionymi w $max_calls_limit .
-
$total_user_calls: Sumuje liczbę przypadków, w których użytkownik żądał dostępu do limitu wywołań w danym okresie czasu.
Następnie dodaj poniższy kod, aby pobrać adres IP żądającego dostępu do strony internetowej:
|
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']; } |
W celach demonstracyjnych ten kod rejestruje działania użytkowników na podstawie ich adresów IP. Jeśli masz na serwerze chroniony zasób, który wymaga uwierzytelnienia, możesz śledzić działania użytkowników za pomocą ich nazw użytkowników lub tokenów dostępu.
W naszym przewodniku każdemu użytkownikowi logującemu się do systemu zostanie przypisany unikalny identyfikator (na przykład identyfikator klienta, programisty, dostawcy, a nawet identyfikator użytkownika). Pamiętaj, aby używać tych identyfikatorów zamiast $adresu IP użytkownika, jeśli postępujesz zgodnie z naszym samouczkiem.
W tym przypadku adres IP użytkownika jest wystarczający do zademonstrowania tej koncepcji. Dodaj poniższy blok kodu do swojego pliku, gdy uzyskasz adres IP użytkownika z poprzedniego fragmentu kodu:
|
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 "Użytkownik " . $user_ip_address . " przekroczył limit."; exit(); } } echo "Witaj " . $user_ip_address . ", łączna liczba wykonanych wywołań: " . $total_user_calls . " w " . $time_period . " sekund"; |
Oto przegląd tych instrukcji:
-
if...else: Instrukcja weryfikuje, czy na serwerze Redis zdefiniowano klucz z adresem IP.
-
Jeśli klucz nie zostanie znaleziony, if (!$redis->exists($user_ip_address)) {...}, możesz ustawić klucz i zdefiniować jego wartość na 1 za pomocą $redis->set($user_ip_address, 1).
-
-
$redis->expire($user_ip_address, $time_period): Przypomina o wygaśnięciu klucza w określonym czasie. W tym samouczku ustawiliśmy go na 15 sekund.
-
Jeśli adres IP użytkownika nie zostanie znaleziony w kluczu Redis, ustaw zmienną $total_user_calls wartość jako 1.
-
else {...}: Blok instrukcji używa polecenia $redis->INCR($user_ip_address); aby zwiększyć wartość klucza Redis o 1. Zostanie to zastosowane do każdego adresu IP powiązanego z kluczem.
-
Note: Możesz to osiągnąć tylko wtedy, gdy klucz jest już ustawiony na serwerze Redis i liczony jako powtarzające się żądanie.
-
-
$total_user_calls = $redis->get($user_ip_address): Ta instrukcja pobiera łączną liczbę żądań poprzez weryfikację odpowiedniego klucza opartego na adresie IP na serwerze Redis.
-
if ($total_user_calls > $max_calls_limit) {... }..: Ta if instrukcja służy do weryfikacji przekroczenia limitu. Jeśli tak, powiadamiasz użytkownika za pomocą echo "Użytkownik" . $user_ip_address . " przekroczył limit.";.
Na koniec powiadamiasz użytkownika o liczbie jego wizyt w określonym okresie za pomocą instrukcji echo "Witaj" . $user_ip_address . " łączna liczba połączeń " . $total_user_calls . " w " . $time_period . " sekund"; .
Następnie dodaj następujące linie kodu w swoim pliku /var/www/html/demo.php:
|
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 "Użytkownik " . $user_ip_address . " przekroczył limit."; exit(); } } echo "Witaj " . $user_ip_address . " łączna liczba połączeń " . $total_user_calls . " w " . $time_period . " sekund"; ?> |
Zapisz i zamknij plik /var/www/html/demo.php po zakończeniu jego modyfikacji. Na stronie internetowej demo.php utworzyłeś teraz logikę wymaganą do ograniczenia liczby żądań użytkowników. Przetestujmy nasz skrypt w kolejnym kroku.
Krok 3: Uruchomienie testu ograniczania liczby żądań w Redis
W tym kroku użyjesz polecenia curl do żądania zasobu internetowego, który napisałeś w Kroku 2. Aby dokładnie przetestować skrypt, wydaj jedno polecenie, które zażąda zasobu pięć razy. Można to osiągnąć poprzez dodanie zastępczego argumentu URL do demo.php koniec pliku. Aby wykonać curl instrukcje pięciokrotnie, użyj wartości ?[1-5] na końcu swojego żądania.
Wykonaj następujące curl polecenie poniżej:
|
1 |
curl -H "Accept: text/plain" -H "Content-Type: text/plain" -X GET http://localhost/demo.php?[1-5] |
Po wykonaniu kodu powinieneś otrzymać coś takiego:
|
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 Witamy 127.0.0.1 łącznie wywołań wykonano 1 w 15 sekund [2/5]: http://localhost/demo.php?2 --> <stdout> --_curl_--http://localhost/demo.php?2 Witamy 127.0.0.1 łącznie wywołań wykonano 2 w 15 sekund [3/5]: http://localhost/demo.php?3 --> <stdout> --_curl_--http://localhost/demo.php?3 Witamy 127.0.0.1 łącznie wywołań wykonano 3 w 15 sekund [4/5]: http://localhost/demo.php?4 --> <stdout> --_curl_--http://localhost/demo.php?4 Użytkownik 127.0.0.1 limit przekroczony. [5/5]: http://localhost/demo.php?5 --> <stdout> --_curl_--http://localhost/demo.php?5 Użytkownik 127.0.0.1 limit przekroczony. |
Pierwsze trzy żądania, jak widać, przebiegły bez zakłóceń. Czwarte i piąte zapytanie zostało jednak ograniczone przez Twój skrypt. Istnieje duże prawdopodobieństwo, że serwer Redis spowalnia prędkość, z jaką użytkownicy mogą wysyłać zapytania.
Wskazałeś niskie wartości dla dwóch zmiennych wymienionych poniżej w tym poradniku:
|
1 2 3 4 |
... $max_calls_limit = 3; $time_period = 15; ... |
Tworząc aplikację w środowisku produkcyjnym, warto pomyśleć o użyciu większych liczb, w zależności od tego, jak często Twoim zdaniem ludzie będą z niej korzystać.
Dobrym pomysłem jest przeanalizowanie statystyk w czasie rzeczywistym przed dostosowaniem tych liczb. W tym przykładzie, jeśli logi serwera pokazują, że przeciętny użytkownik odwiedza Twoją aplikację 1000 razy co 60 sekund, możesz użyć tej liczby jako przykładu poziomu ograniczenia przepustowości.
Podsumowanie
Z tego poradnika dowiedziałeś się, jak korzystać z serwera Redis z PHP na Ubuntu 20.04. Chociaż ten wpis pokazuje, jak działa ograniczanie liczby żądań (rate limiting) w Redis, możesz go dostosować do potrzeb swojej aplikacji internetowej. Zachęcamy do zapoznania się z rzeczywistymi przykładami, takimi jak maksymalny limit żądań Twittera, Google Custom Search JSON API, oraz inną podobną dokumentacją, aby poszerzyć swoją wiedzę na temat ograniczania liczby żądań i samodzielnie poeksperymentować z różnymi limitami czasu.
Ponadto istnieje wiele innych materiałów szkoleniowych dotyczących Redis i PHP, do których można uzyskać dostęp z naszych blogów:
- Wdrażanie aplikacji PHP w klastrze Kubernetes z Ubuntu 18.04
- Instalacja i zabezpieczanie phpMyAdmin na Ubuntu 20.04
- Jak zainstalować stos LEMP (Linux Nginx MySQL PHP) na Ubuntu 20.04
Udanego kodowania!
Komentarze
Brak komentarzy. Bądź pierwszy.