어떻게 Ubuntu 20.04에서 Redis를 사용하여 PHP 속도 제한을 실현합니까

어떻게 Ubuntu 20.04에서 Redis를 사용하여 PHP 속도 제한을 실현합니까

2022-06-01 last update

27 minutes reading Redis PHP Ubuntu 20.04 LAMP Stack
저자는 Apache Software FoundationWrite for DOnations 계획의 일부로 기부를 받았다.

소개


Redis(원격 사전 서버)는 메모리의 소스 오픈 소프트웨어입니다.서버 RAM을 사용하는 데이터 패브릭 스토리지로 가장 빠른 SSD(Solid State Drive)보다 몇 배 빠른 속도를 제공합니다.이를 통해 Redis는 높은 응답성을 가지므로 속도 제한에 적합합니다.
속도 제한은 사용자가 서버에서 자원을 요청하는 횟수를 제한하는 기술이다.많은 서비스들이 사용자가 서버에 너무 많은 부하를 불러올 때 서비스를 남용하는 것을 방지하기 위해 속도 제한을 실현했다.
예를 들어 API (Application Programming Interface)을 웹 응용 프로그램에 사용하여 공공 PHP을 실현할 때 어떤 형식의 속도 제한이 필요합니다.API를 게시할 때 특정 시간대에 애플리케이션 사용자가 반복적으로 작업하는 횟수를 제어하고자 하기 때문입니다.만약 아무런 제어가 없다면, 사용자는 시스템을 완전히 정지시킬 수 있습니다.
특정 제한을 초과한 사용자 요청을 거부하면 프로그램이 안정적으로 실행될 수 있습니다.만약 고객이 많다면, 속도 제한은 공평한 사용 정책을 강제하여 모든 고객이 당신의 응용 프로그램에 고속으로 접근할 수 있도록 할 것입니다.속도 제한은 대역폭 비용을 낮추고 서버 혼잡을 최소화하는 데도 도움이 된다.
ySQL과 같은 데이터베이스에 사용자 활동을 기록하여 속도 제한 모듈을 작성하는 것이 가능할 수 있습니다.그러나 많은 사용자가 시스템에 접근할 때 최종 제품은 디스크에서 데이터를 추출하고 설정된 제한과 비교해야 하기 때문에 확장할 수 없습니다.이것은 속도가 느릴 뿐만 아니라 관계 데이터베이스 관리 시스템도 이 목적을 위해 설계된 것이 아니다.
Redis는 메모리의 데이터베이스로 작동하기 때문에 속도 제한기를 만드는 합격 후보입니다. proven reliable for this purpose입니다.
이 강좌에서는 Ubuntu 20.04 서버에서 Redis를 사용하여 속도 제한에 사용되는 PHP 스크립트를 실행합니다.

선결 조건


시작하기 전에 다음이 필요합니다.

  • Ubuntu 20.04 서버와 sudo 권한이 있는 비root 사용자.서버를 설정하고 새 사용자를 만들려면 Initial Server Setup with Ubuntu 20.04 안내서를 참조하십시오.

  • 등잔더미.How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 20.04을 따르다.이 안내서에서 4 단계를 건너뛰고 웹 사이트에 가상 호스트를 만들고 아파치 설치에서 만든 기본 가상 호스트를 사용할 수 있습니다.

  • Redis 서버.How To Install and Secure Redis on Ubuntu 20.04 - Quickstart 강좌에 따라 설정합니다.
  • 1단계 - PHP용 Redis 라이브러리 설치


    먼저 Ubuntu 패키지 저장소 인덱스를 업데이트합니다.그리고 php-redis 연장부품을 설치합니다.이것은 PHP 코드에서 Redis를 구현할 수 있는 라이브러리입니다.이렇게 하려면 다음 명령을 실행하십시오.
    1. sudo apt update
    2. sudo apt install -y php-redis
    그런 다음 Apache 서버를 다시 시작하여 php-redis 라이브러리를 로드합니다.
    1. sudo systemctl restart apache2
    소프트웨어 정보 인덱스가 업데이트되고 PHP용 Redis 라이브러리가 설치되면 사용자의 IP 주소에 따라 사용자의 접근 권한을 제한하는 PHP 자원을 만들 수 있습니다.

    2단계 - 속도 제한을 위한 PHP 웹 리소스 구축


    이 단계에서 웹 서버의 루트 디렉터리 (test.php) 에 /var/www/html/ 파일을 만들 것입니다.대중은 이 파일에 접근할 수 있으며, 사용자는 웹 브라우저에 주소를 입력하여 실행할 수 있습니다.단, 이 안내서의 기초로 잠시 후 curl 명령을 사용하여 자원에 대한 접근을 테스트할 것입니다.
    예시 자원 파일은 사용자가 10초 안에 세 번 접근할 수 있도록 합니다.이 제한을 초과하려고 하는 사용자는 속도 제한을 받았다고 알리는 오류를 받을 것입니다.
    이 파일의 핵심 기능은 대부분 Redis 서버에 의존합니다.사용자가 처음으로 리소스를 요청하면 파일의 PHP 코드는 사용자의 IP 주소에 따라 Redis 서버에 키를 생성합니다.
    사용자가 리소스에 다시 액세스하면 PHP 코드는 사용자의 IP 주소를 Redis 서버에 저장된 키와 일치시키려고 시도하고 키가 있으면 값을 1 증가시킵니다.PHP 코드는 증가된 값이 설정의 최대 한계에 도달했는지 계속 확인합니다.
    사용자 IP 주소 기반 Redis 키는 10초 후에 만료됩니다.이 시간 이후 웹 자원에 대한 사용자의 접근을 다시 기록하기 시작합니다.
    먼저 /var/www/html/test.php 파일을 엽니다.
    1. sudo nano /var/www/html/test.php
    다음 정보를 입력하여 Redis 클래스를 초기화합니다.REDIS_PASSWORD에 적절한 값을 입력해야 합니다.
    /var/www/html/test.php
    <?php
    
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $redis->auth('REDIS_PASSWORD');
    
    $redis->auth은 Redis 서버에 대한 일반 텍스트 인증을 수행합니다.로컬에서 작업할 때 (localhost) 가능합니다. 그러나 원격 Redis 서버를 사용하는 경우 SSL authentication을 사용하십시오.
    그런 다음 같은 파일에서 다음 변수를 초기화합니다.
    /var/www/html/test.php
    . . .
    $max_calls_limit  = 3;
    $time_period      = 10;
    $total_user_calls = 0;
    
    정의:
  • $max_calls_limit: 사용자가 자원에 접근할 수 있는 최대 호출 수입니다.
  • $time_period: 사용자가 $max_calls_limit에 따라 자원에 접근할 수 있는 시간 범위(초)를 정의합니다.
  • $total_user_calls: 사용자가 주어진 시간대에 자원에 접근을 요청한 횟수를 검색하는 변수를 초기화합니다.
  • 다음 코드를 추가하여 웹 리소스를 요청한 사용자의 IP 주소를 검색합니다.
    /var/www/html/test.php
    . . .
    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'];
    }
    
    이 코드는 사용자의 IP 주소를 사용하여 프레젠테이션을 하지만, 서버에 인증이 필요한 보호된 자원이 있으면 사용자의 사용자 이름이나 영패에 접근하여 사용자의 활동을 기록할 수 있습니다.
    이 경우 인증을 통해 시스템에 접속하는 모든 사용자는 고유 식별자(예: 고객 ID, 개발자 ID, 공급업체 ID, 심지어 사용자 ID)를 갖게 됩니다.(이를 구성하려면 $user_ip_address 대신 이 식별자를 사용하도록 주의하십시오.)
    이 안내서의 사용자 IP 주소는 이 개념을 증명하기에 충분하다.따라서 이전 코드 세그먼트에서 사용자의 IP 주소를 검색한 후 파일에 다음 코드 블록을 추가합니다.
    /var/www/html/test.php
    . . .
    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 "User " . $user_ip_address . " limit exceeded.";
            exit();
        }
    }
    
    echo "Welcome " . $user_ip_address . " total calls made " . $total_user_calls . " in " . $time_period . " seconds";
    
    이 코드에서 if...else 문구를 사용하여 Redis 서버에 IP 주소로 정의된 키가 있는지 확인합니다.키 if (!$redis->exists($user_ip_address)) {...}이 존재하지 않으면 이 값을 설정하고 코드 1을 사용하여 $redis->set($user_ip_address, 1);으로 정의합니다.$redis->expire($user_ip_address, $time_period);은 키를 이 예에서 10초 동안 만료되도록 설정합니다.
    사용자의 IP 주소가 Redis 키로 존재하지 않으면 변수 $total_user_calls1으로 설정합니다....else {...}... 문 블록에서 $redis->INCR($user_ip_address); 명령을 사용하여 각 IP 주소 키의 Redis 키 세트 값을 1 증가시킵니다.Redis 서버에서 키를 설정하고 이를 중복 요청으로 계산한 경우에만 발생합니다.
    문장 $total_user_calls = $redis->get($user_ip_address);은 Redis 서버에서 IP 주소를 기반으로 하는 키를 검사하여 사용자가 보낸 요청 총수를 검색합니다.
    파일 끝에 ...if ($total_user_calls > $max_calls_limit) {... }.. 문장을 사용하여 제한을 초과했는지 확인합니다.만약 그렇다면 echo "User " . $user_ip_address . " limit exceeded.";을 사용하여 사용자에게 통지하십시오.마지막으로 echo "Welcome " . $user_ip_address . " total calls made " . $total_user_calls . " in " . $time_period . " seconds"; 문구를 사용하여 사용자가 이 시간 내에 접근할 수 있음을 알립니다.
    모든 코드를 추가하면 /var/www/html/test.php 파일은 다음과 같습니다.
    /var/www/html/test.php
    <?php
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $redis->auth('REDIS_PASSWORD');
    
    $max_calls_limit  = 3;
    $time_period      = 10;
    $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 "User " . $user_ip_address . " limit exceeded.";
            exit();
        }
    }
    
    echo "Welcome " . $user_ip_address . " total calls made " . $total_user_calls . " in " . $time_period . " seconds";
    
    /var/www/html/test.php 파일을 편집한 후 저장하고 닫습니다.
    현재 test.php 웹 자원의 사용자를 등급별로 나누는 데 필요한 논리를 작성했습니다.다음 단계에서 스크립트를 테스트할 것입니다.

    3단계 - Redis 속도 제한 테스트


    이 단계에서 curl 명령을 사용하여 2단계에서 인코딩된 웹 자원을 요청합니다.스크립트를 완전히 검사하려면 명령에서 자원을 다섯 번 요청합니다.test.php 파일의 끝에 자리 표시자 URL 매개 변수가 포함되어 있습니다.여기서 요청이 끝날 때 ?[1-5] 값을 사용하여 curl 명령을 다섯 번 실행할 수 있습니다.
    다음 명령을 실행합니다.
    1. curl -H "Accept: text/plain" -H "Content-Type: text/plain" -X GET http://localhost/test.php?[1-5]
    코드를 실행하면 다음과 같은 출력을 받을 수 있습니다.
    Output
    [1/5]: http://localhost/test.php?1 --> <stdout> --_curl_--http://localhost/test.php?1 Welcome 127.0.0.1 total calls made 1 in 10 seconds [2/5]: http://localhost/test.php?2 --> <stdout> --_curl_--http://localhost/test.php?2 Welcome 127.0.0.1 total calls made 2 in 10 seconds [3/5]: http://localhost/test.php?3 --> <stdout> --_curl_--http://localhost/test.php?3 Welcome 127.0.0.1 total calls made 3 in 10 seconds [4/5]: http://localhost/test.php?4 --> <stdout> --_curl_--http://localhost/test.php?4 User 127.0.0.1 limit exceeded. [5/5]: http://localhost/test.php?5 --> <stdout> --_curl_--http://localhost/test.php?5 User 127.0.0.1 limit exceeded.
    보시다시피 세 번째 요청이 실행되었을 때 문제가 발생하지 않았습니다.단, 스크립트는 네 번째와 다섯 번째 요청의 속도를 제한합니다.이것은 Redis 서버가 사용자가 요청한 속도를 제한하고 있음을 확인합니다.
    이 설명서에서는 다음 두 변수에 대해 낮은 값을 설정합니다.
    /var/www/html/test.php
    ...
    $max_calls_limit  = 3;
    $time_period      = 10;
    ...
    
    운영 환경에서 응용 프로그램을 설계할 때 사용자가 응용 프로그램에 접근하기를 원하는 빈도에 따라 더 높은 값을 고려할 수 있습니다.
    가장 좋은 방법은 이 값을 설정하기 전에 실시간 통계 정보를 검사하는 것이다.예를 들어 서버 로그에 평균 60초당 1000번의 사용자가 응용 프로그램에 접근한다고 표시되면 사용자를 제한하는 기준으로 사용할 수 있습니다.
    상황을 더욱 잘 이해하기 위해 다음은 실제 속도 제한 실시 예시들(2021까지)이다.
    매일
  • Twitter allows only 100,000 requests에서 그 /statuses/mentions_timeline/statuses/user_timeline의 종점에 도착한다.
  • DigitalOcean allows 5,000 requests per hour은 API 단점에서 OAuth 영패를 사용하여 인증을 하는 모든 사용자에게 사용됩니다.
  • The Google Custom Search JSON API은 매일 100회 무료로 검색 조회를 할 수 있습니다.
  • 결론


    이 강좌는 웹 응용 프로그램의 의외나 악의적인 과도한 사용을 방지하기 위해 Ubuntu 20.04 서버에서 Redis를 사용하여 속도 제한에 사용되는 PHP 스크립트를 구현합니다.사용자의 용례에 따라 코드를 확장하여 사용자의 수요를 더욱 만족시킬 수 있습니다.
    생산용으로 Apache 서버를 보호하고자 할 수 있습니다.How To Secure Apache with Let’s Encrypt on Ubuntu 20.04 강좌를 따르다.
    Redis를 데이터베이스 캐시로 읽는 방법도 고려할 수 있습니다.우리의 How To Set Up Redis as a Cache for MySQL with PHP on Ubuntu 20.04 강좌를 사용해 보세요.
    PHPRedis 테마 페이지에서 더 많은 자원을 찾을 수 있습니다.