복붙노트

PHP 작업을 비동기 적으로 실행하십시오.

PHP

PHP 작업을 비동기 적으로 실행하십시오.

다소 큰 웹 응용 프로그램에서 작업하고 백엔드는 대부분 PHP로되어 있습니다. 코드에서 작업을 완료해야하는 부분이 몇 개 있지만 사용자가 결과를 기다리는 것을 원하지는 않습니다. 예를 들어 새 계정을 만들 때 환영 이메일을 보내야합니다. 그러나 'Finish Registration'버튼을 눌렀을 때 전자 메일이 실제로 전송 될 때까지 기다리지 않고 그냥 프로세스를 시작하고 즉시 사용자에게 메시지를 보내려합니다.

지금까지 어떤 곳에서는 exec ()를 사용하여 해킹 된 느낌을 사용했습니다. 기본적으로 다음과 같은 작업을 수행합니다.

exec("doTask.php $arg1 $arg2 $arg3 >/dev/null 2>&1 &");

어느 쪽이 효과가있는 것처럼 보이지만 더 좋은 방법이 있는지 궁금합니다. 필자는 MySQL 테이블에 작업을 대기시키는 시스템을 작성하고,이 테이블을 한 초에 한 번씩 쿼리하고 발견 한 새로운 작업을 실행하는 별도의 장기 실행 PHP 스크립트를 작성하려고합니다. 또한 필요한 경우 나중에 여러 작업자 시스템간에 작업을 분할 할 수있는 이점이 있습니다.

나는 바퀴를 다시 발명하고 있나? exec () 해킹이나 MySQL 큐보다 나은 해결책이 있습니까?

해결법

  1. ==============================

    1.필자는 대기열 접근 방식을 사용했으며, 서버로드가 유휴 상태가 될 때까지 처리를 연기 할 수 있으므로 잘 작동합니다. "긴급하지 않은 작업"을 쉽게 파티션 할 수 있다면로드를 매우 효과적으로 관리 할 수 ​​있습니다.

    필자는 대기열 접근 방식을 사용했으며, 서버로드가 유휴 상태가 될 때까지 처리를 연기 할 수 있으므로 잘 작동합니다. "긴급하지 않은 작업"을 쉽게 파티션 할 수 있다면로드를 매우 효과적으로 관리 할 수 ​​있습니다.

    자신 만의 롤링은 너무 까다 롭지 않습니다. 체크 아웃 할 수있는 몇 가지 옵션이 있습니다.

    또 다른 방법은 ignore_user_abort를 사용하는 것입니다. 사용자에게 페이지를 보내면 사용자의 페이지로드가 길어지는 효과가 있지만 너무 조기 종료 될 염려없이 최종 처리를 수행 할 수 있습니다 원근법.

  2. ==============================

    2.응답을 기다릴 필요없이 하나 또는 여러 개의 HTTP 요청을 실행하려는 경우 간단한 PHP 솔루션도 있습니다.

    응답을 기다릴 필요없이 하나 또는 여러 개의 HTTP 요청을 실행하려는 경우 간단한 PHP 솔루션도 있습니다.

    호출 스크립트에서 :

    $socketcon = fsockopen($host, 80, $errno, $errstr, 10);
    if($socketcon) {   
       $socketdata = "GET $remote_house/script.php?parameters=... HTTP 1.1\r\nHost: $host\r\nConnection: Close\r\n\r\n";      
       fwrite($socketcon, $socketdata); 
       fclose($socketcon);
    }
    // repeat this with different parameters as often as you like
    

    호출 된 script.php에서 첫 번째 줄에서 다음 PHP 함수를 호출 할 수 있습니다.

    ignore_user_abort(true);
    set_time_limit(0);
    

    이렇게하면 HTTP 연결이 닫힐 때 스크립트가 시간 제한없이 계속 실행됩니다.

  3. ==============================

    3.프로세스를 포크하는 또 다른 방법은 말리기입니다. 내부 업무를 웹 서비스로 설정할 수 있습니다. 예 :

    프로세스를 포크하는 또 다른 방법은 말리기입니다. 내부 업무를 웹 서비스로 설정할 수 있습니다. 예 :

    그런 다음 사용자가 액세스 한 스크립트에서 서비스 호출을합니다.

    $service->addTask('t1', $data); // post data to URL via curl
    

    서비스는 mysql을 사용하여 작업 대기열을 추적 할 수 있습니다. 요점은 서비스 내에 포장되어 스크립트가 URL을 소비하는 것입니다. 이렇게하면 필요한 경우 다른 시스템 / 서버로 서비스를 이동할 수 있습니다 (즉, 쉽게 확장 가능).

    http 권한 또는 Amazon의 웹 서비스와 같은 사용자 정의 권한 부여 스키마를 추가하면 다른 사람 / 서비스 (원하는 경우)에서 작업을 열어 추가 작업을 수행하고 위에 모니터링 서비스를 추가하여 작업을 추적 할 수 있습니다 대기열 및 작업 상태.

    약간의 설치 작업이 필요하지만 많은 이점이 있습니다.

  4. ==============================

    4.Beanstalkd를 한 프로젝트에 사용하고 다시 계획했습니다. 비동기 프로세스를 실행하는 훌륭한 방법이라는 것을 알았습니다.

    Beanstalkd를 한 프로젝트에 사용하고 다시 계획했습니다. 비동기 프로세스를 실행하는 훌륭한 방법이라는 것을 알았습니다.

    내가 한 일은 다음과 같습니다.

    예를 들어 QueueTask ( '/ image / resize / filename / example.jpg')라고하는 이미지의 크기를 조정할 수 있도록 '멋진'URL을 디코딩하는 Zend-Framework 기반 시스템을 작성했습니다. URL은 먼저 배열 (모듈, 컨트롤러, 액션, 매개 변수)로 디코딩 된 다음 큐 자체에 주입하기 위해 JSON으로 변환되었습니다.

    장시간 실행되는 cli 스크립트는 대기열에서 작업을 집어 들고 (Zend_Router_Simple을 통해) 실행하고 필요한 경우 memcached에 정보를 입력하여 PHP가 완료 될 때 필요한대로 픽업합니다.

    한 가지 주름은 cli 스크립트가 다시 시작하기 전에 50 개의 루프 만 실행했지만 계획대로 다시 시작하려는 경우 즉시 수행 할 것입니다 (bash 스크립트를 통해 실행). 문제가 있고 exit (0) (exit의 기본값 또는 die ();)를 수행 한 경우 처음 몇 초 동안 일시 중지됩니다.

  5. ==============================

    5.값 비싼 작업을 제공하는 것에 관한 질문이라면 php-fpm이 지원되는데 왜 fastcgi_finish_request () 함수를 사용하지 않을까요?

    값 비싼 작업을 제공하는 것에 관한 질문이라면 php-fpm이 지원되는데 왜 fastcgi_finish_request () 함수를 사용하지 않을까요?

    이런 식으로 비동기를 실제로 사용하지는 않습니다.

    다시 한번 php-fpm이 필요합니다.

  6. ==============================

    6.다음은 웹 애플리케이션 용으로 코딩 된 간단한 클래스입니다. 그것은 포크 PHP 스크립트 및 기타 스크립트 수 있습니다. UNIX 및 Windows에서 작동합니다.

    다음은 웹 애플리케이션 용으로 코딩 된 간단한 클래스입니다. 그것은 포크 PHP 스크립트 및 기타 스크립트 수 있습니다. UNIX 및 Windows에서 작동합니다.

    class BackgroundProcess {
        static function open($exec, $cwd = null) {
            if (!is_string($cwd)) {
                $cwd = @getcwd();
            }
    
            @chdir($cwd);
    
            if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
                $WshShell = new COM("WScript.Shell");
                $WshShell->CurrentDirectory = str_replace('/', '\\', $cwd);
                $WshShell->Run($exec, 0, false);
            } else {
                exec($exec . " > /dev/null 2>&1 &");
            }
        }
    
        static function fork($phpScript, $phpExec = null) {
            $cwd = dirname($phpScript);
    
            @putenv("PHP_FORCECLI=true");
    
            if (!is_string($phpExec) || !file_exists($phpExec)) {
                if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
                    $phpExec = str_replace('/', '\\', dirname(ini_get('extension_dir'))) . '\php.exe';
    
                    if (@file_exists($phpExec)) {
                        BackgroundProcess::open(escapeshellarg($phpExec) . " " . escapeshellarg($phpScript), $cwd);
                    }
                } else {
                    $phpExec = exec("which php-cli");
    
                    if ($phpExec[0] != '/') {
                        $phpExec = exec("which php");
                    }
    
                    if ($phpExec[0] == '/') {
                        BackgroundProcess::open(escapeshellarg($phpExec) . " " . escapeshellarg($phpScript), $cwd);
                    }
                }
            } else {
                if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
                    $phpExec = str_replace('/', '\\', $phpExec);
                }
    
                BackgroundProcess::open(escapeshellarg($phpExec) . " " . escapeshellarg($phpScript), $cwd);
            }
        }
    }
    
  7. ==============================

    7.이것은 제가 지금 몇 년 동안 사용해 왔던 것과 같은 방법이며, 더 나은 것을 보거나 찾지 못했습니다. 사람들이 말했듯이, PHP는 단일 쓰레드이기 때문에 할 수있는 일이별로 없습니다.

    이것은 제가 지금 몇 년 동안 사용해 왔던 것과 같은 방법이며, 더 나은 것을 보거나 찾지 못했습니다. 사람들이 말했듯이, PHP는 단일 쓰레드이기 때문에 할 수있는 일이별로 없습니다.

    나는 실제로 이것에 하나의 추가 레벨을 추가했고 그것은 프로세스 ID를 얻고 저장하고있다. 이렇게하면 다른 페이지로 리디렉션하고 사용자가 해당 페이지에 앉아서 AJAX를 사용하여 프로세스가 완료되었는지 (프로세스 ID는 더 이상 존재하지 않는지) 확인할 수 있습니다. 이것은 스크립트 길이가 브라우저의 시간 초과를 유발하지만 사용자가 스크립트가 다음 단계 전에 완료 될 때까지 기다려야하는 경우에 유용합니다. (필자의 경우 CSV로 파일을 처리하는 대형 ZIP 파일은 데이터베이스에 최대 30,000 개의 레코드를 추가하여 사용자가 정보를 확인해야합니다.)

    또한 보고서 생성을 위해 비슷한 프로세스를 사용했습니다. 나는 느린 SMTP에 진짜 문제가 없다면 이메일과 같은 것에 "배경 처리"를 사용할 것인지 확신하지 못한다. 대신 테이블을 대기열로 사용하고 매분마다 대기열에서 이메일을 전송하는 프로세스를 가질 수 있습니다. 이메일을 두 번 보내거나 다른 유사한 문제를 일으킬 필요가 있습니다. 다른 작업에 대해서도 비슷한 큐잉 프로세스를 고려할 것입니다.

  8. ==============================

    8.PHP에는 기본적으로 활성화되어 있지 않은 다중 스레드가 있습니다. 정확히 pthreads라는 확장이 있습니다. PHP는 ZTS로 컴파일해야합니다. (스레드 안전) 모래밭:

    PHP에는 기본적으로 활성화되어 있지 않은 다중 스레드가 있습니다. 정확히 pthreads라는 확장이 있습니다. PHP는 ZTS로 컴파일해야합니다. (스레드 안전) 모래밭:

    예제들

    다른 튜토리얼

    PTHL 확장 PTHL

  9. ==============================

    9.rojoca에서 제안한대로 cURL을 사용하는 것이 좋습니다.

    rojoca에서 제안한대로 cURL을 사용하는 것이 좋습니다.

    다음은 그 예입니다. 스크립트가 백그라운드에서 실행되는 동안 text.txt를 모니터링 할 수 있습니다.

    <?php
    
    function doCurl($begin)
    {
        echo "Do curl<br />\n";
        $url = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
        $url = preg_replace('/\?.*/', '', $url);
        $url .= '?begin='.$begin;
        echo 'URL: '.$url.'<br>';
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        echo 'Result: '.$result.'<br>';
        curl_close($ch);
    }
    
    
    if (empty($_GET['begin'])) {
        doCurl(1);
    }
    else {
        while (ob_get_level())
            ob_end_clean();
        header('Connection: close');
        ignore_user_abort();
        ob_start();
        echo 'Connection Closed';
        $size = ob_get_length();
        header("Content-Length: $size");
        ob_end_flush();
        flush();
    
        $begin = $_GET['begin'];
        $fp = fopen("text.txt", "w");
        fprintf($fp, "begin: %d\n", $begin);
        for ($i = 0; $i < 15; $i++) {
            sleep(1);
            fprintf($fp, "i: %d\n", $i);
        }
        fclose($fp);
        if ($begin < 10)
            doCurl($begin + 1);
    }
    
    ?>
    
  10. ==============================

    10.불행히도 PHP에는 네이티브 스레딩 기능이 없습니다. 그래서이 경우에는 당신이하고 싶은 것을하기 위해 어떤 종류의 커스텀 코드를 사용하는 것 외에는 선택의 여지가 없다고 생각합니다.

    불행히도 PHP에는 네이티브 스레딩 기능이 없습니다. 그래서이 경우에는 당신이하고 싶은 것을하기 위해 어떤 종류의 커스텀 코드를 사용하는 것 외에는 선택의 여지가 없다고 생각합니다.

    인터넷에서 PHP 스레딩을 검색하면 일부 사람들은 PHP에서 스레드를 시뮬레이트하는 방법을 생각해 냈습니다.

  11. ==============================

    11."Thank You For Registering"응답에 Content-Length HTTP 헤더를 설정하면 브라우저는 지정된 바이트 수가 수신 된 후 연결을 닫아야합니다. 이로 인해 서버 측 프로세스가 실행되고 (ignore_user_abort가 설정되었다고 가정) 최종 사용자 대기없이 작업을 완료 할 수 있습니다.

    "Thank You For Registering"응답에 Content-Length HTTP 헤더를 설정하면 브라우저는 지정된 바이트 수가 수신 된 후 연결을 닫아야합니다. 이로 인해 서버 측 프로세스가 실행되고 (ignore_user_abort가 설정되었다고 가정) 최종 사용자 대기없이 작업을 완료 할 수 있습니다.

    물론 헤더를 렌더링하기 전에 응답 내용의 크기를 계산해야하지만 짧은 응답 (문자열에 출력 작성, strlen () 호출, header () 호출, string 렌더링)을 사용하면 매우 쉽습니다.

    이 방법은 "프런트 엔드"큐를 관리하지 않아도된다는 이점이 있습니다. HTTP 자식 프로세스가 서로 밟기를 막기 위해 백 엔드에서 일부 작업을 수행해야 할 수도 있지만, 이미 수행해야 할 작업입니다 , 어쨌든.

  12. ==============================

    12.만약 당신이 완전히 날려 ActiveMQ 싶지 않아, 나는 RabbitMQ 고려하는 것이 좋습니다. RabbitMQ는 AMQP 표준을 사용하는 경량 메시징입니다.

    만약 당신이 완전히 날려 ActiveMQ 싶지 않아, 나는 RabbitMQ 고려하는 것이 좋습니다. RabbitMQ는 AMQP 표준을 사용하는 경량 메시징입니다.

    또한 AMQP 기반 메시지 브로커에 액세스하는 인기있는 AMQP 클라이언트 라이브러리 인 php-amqplib을 살펴볼 것을 권장합니다.

  13. ==============================

    13.난 당신이 모든 페이지가 비동기로 각 페이지 응답을 기다리지 않고 한 번에 독립적으로 실행되는 것처럼 많은 페이지를 호출하는 데 도움이 될 것 입니다이 기술을 시도해야한다고 생각합니다.

    난 당신이 모든 페이지가 비동기로 각 페이지 응답을 기다리지 않고 한 번에 독립적으로 실행되는 것처럼 많은 페이지를 호출하는 데 도움이 될 것 입니다이 기술을 시도해야한다고 생각합니다.

    cornjobpage.php // mainpage

        <?php
    
    post_async("http://localhost/projectname/testpage.php", "Keywordname=testValue");
    //post_async("http://localhost/projectname/testpage.php", "Keywordname=testValue2");
    //post_async("http://localhost/projectname/otherpage.php", "Keywordname=anyValue");
    //call as many as pages you like all pages will run at once independently without waiting for each page response as asynchronous.
                ?>
                <?php
    
                /*
                 * Executes a PHP page asynchronously so the current page does not have to wait for it to     finish running.
                 *  
                 */
                function post_async($url,$params)
                {
    
                    $post_string = $params;
    
                    $parts=parse_url($url);
    
                    $fp = fsockopen($parts['host'],
                        isset($parts['port'])?$parts['port']:80,
                        $errno, $errstr, 30);
    
                    $out = "GET ".$parts['path']."?$post_string"." HTTP/1.1\r\n";//you can use POST instead of GET if you like
                    $out.= "Host: ".$parts['host']."\r\n";
                    $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
                    $out.= "Content-Length: ".strlen($post_string)."\r\n";
                    $out.= "Connection: Close\r\n\r\n";
                    fwrite($fp, $out);
                    fclose($fp);
                }
                ?>
    

    testpage.php

        <?
        echo $_REQUEST["Keywordname"];//case1 Output > testValue
        ?>
    

    추신 : 루프 매개 변수로 URL 매개 변수를 보내려면 다음 답변을 따르십시오. https : //stackoverflow.com/a/41225209/6295712

  14. ==============================

    14.exec ()를 사용하거나 curl을 사용하여 다른 서버에서 직접 새 프로세스를 생성하면 모든 것을 제대로 처리 할 수 ​​없습니다. exec를 사용하면 기본적으로 서버가 아닌 다른 웹에서 처리 할 수있는 장기 실행 프로세스로 서버를 채 웁니다. 로드 밸런싱과 같은 일을하지 않는 한 컬을 사용하면 다른 서버를 묶을 수 있습니다.

    exec ()를 사용하거나 curl을 사용하여 다른 서버에서 직접 새 프로세스를 생성하면 모든 것을 제대로 처리 할 수 ​​없습니다. exec를 사용하면 기본적으로 서버가 아닌 다른 웹에서 처리 할 수있는 장기 실행 프로세스로 서버를 채 웁니다. 로드 밸런싱과 같은 일을하지 않는 한 컬을 사용하면 다른 서버를 묶을 수 있습니다.

    나는 몇 가지 상황에서 독일어를 사용했으며 이런 종류의 유스 케이스에서는 더 나은 것을 알았다. 단일 작업 대기열 서버를 사용하여 기본적으로 서버에서 수행해야하는 모든 작업의 ​​대기열을 처리하고 필요에 따라 작업자 프로세스의 인스턴스를 실행할 수있는 작업자 서버를 회전시킬 수 있습니다. 필요한 경우 작업자 서버를 실행하고 필요하지 않을 때 작업을 중지하십시오. 또한 필요할 때 작업자 프로세스를 완전히 종료하고 작업자가 다시 온라인 상태가 될 때까지 작업을 대기시킵니다.

  15. ==============================

    15.PHP는 단일 스레드 언어이므로 exec 또는 popen을 사용하는 것 외에 비동기 프로세스를 시작하는 공식적인 방법은 없습니다. 여기에 대한 블로그 게시물이 있습니다. MySQL의 대기열에 대한 아이디어는 좋은 생각입니다.

    PHP는 단일 스레드 언어이므로 exec 또는 popen을 사용하는 것 외에 비동기 프로세스를 시작하는 공식적인 방법은 없습니다. 여기에 대한 블로그 게시물이 있습니다. MySQL의 대기열에 대한 아이디어는 좋은 생각입니다.

    여기에 귀하의 특정 요구 사항은 사용자에게 전자 메일을 보내는 것입니다. 이메일을 보내는 것이 매우 간단하고 빠른 작업이기 때문에 왜 비동기 적으로 그렇게하려고하는지 궁금합니다. 이메일을 많이 보내고 ISP가 스팸 혐의로 귀하를 차단하고 있다면 그것이 대기열에 올릴 이유 중 하나 일지 모르지만 그 외에는 이런 식으로 할 이유가 없다고 생각합니다.

  16. from https://stackoverflow.com/questions/858883/run-php-task-asynchronously by cc-by-sa and MIT license