장기 실행 PHP 스크립트를 관리하는 가장 좋은 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/2212635

  •  19-09-2019
  •  | 
  •  

문제

완료하는 데 오랜 시간(5~30분)이 걸리는 PHP 스크립트가 있습니다.중요한 경우를 대비해 스크립트는 컬을 사용하여 다른 서버에서 데이터를 긁어냅니다.이것이 너무 오래 걸리는 이유입니다.각 페이지를 처리하고 다음 페이지로 이동하기 전에 로드될 때까지 기다려야 합니다.

나는 스크립트를 시작하고 완료될 때까지 그대로 두기를 원합니다. 그러면 데이터베이스 테이블에 플래그가 설정됩니다.

내가 알아야 할 것은 스크립트 실행이 완료되기 전에 http 요청을 종료할 수 있는 방법입니다.또한, PHP 스크립트가 이를 수행하는 가장 좋은 방법입니까?

도움이 되었습니까?

해결책

확실히 PHP로 수행 할 수는 있지만 배경 작업으로이를 수행해서는 안됩니다. 새로운 프로세스는 시작된 프로세스 그룹에서 분리되어야합니다.

사람들은이 FAQ에 대해 동일한 답변을 계속 제공하기 때문에 여기에 더 많은 답변을 썼습니다.

http://symcbean.blogspot.com/2010/02/php-and-long-running-processes.html

의견에서 :

짧은 버전은입니다 shell_exec('echo /usr/bin/php -q longThing.php | at now'); 그러나 여기에 포함시키는 데 약간 긴 이유가 있습니다.

다른 팁

빠르고 더러운 방법은 ignore_user_abort PHP의 기능. 이것은 기본적으로 다음과 같이 말합니다. 사용자가하는 일을 신경 쓰지 말고이 스크립트가 끝날 때 까지이 스크립트를 실행하십시오. 공개 얼굴 사이트라면 다소 위험합니다 (가능하기 때문에 20 번 시작하면 20 ++ 버전의 스크립트가 실행되면 20 번 실행됩니다).

"깨끗한"방법 (적어도 IMHO)은 프로세스를 시작하고 매시간 (또는 그에 따라 크론 조브)를 실행하려면 플래그가 설정되어 있는지 확인할 때 플래그 (예 : DB에서)를 설정하는 것입니다. 설정된 경우 장기 실행 스크립트가 시작되면 설정되지 않으면 Nothin이 발생합니다.

당신은 사용할 수 있습니다 exec 또는 체계 배경 작업을 시작한 다음 작업을 수행합니다.

또한 웹을 사용하는 웹을 긁어내는 더 나은 접근 방식이 있습니다. 나사산 접근법 (한 번에 한 페이지 씩 다중 스레드) 또는 eventLoop (한 번에 여러 페이지를 수행하는 스레드)를 사용하여 하나의 스레드를 사용할 수 있습니다. Perl을 사용한 나의 개인적인 접근 방식은 사용하고 있습니다 Anyevent :: http.

ETA : Symcbean 배경 프로세스를 올바르게 분리하는 방법을 설명했습니다 여기.

아니요, PHP가 최상의 솔루션이 아닙니다.

루비 나 펄에 대해 잘 모르겠지만 파이썬을 사용하면 페이지 스크레이퍼를 멀티 스레드로 다시 쓸 수 있으며 아마도 최소 20 배 더 빠르게 실행될 것입니다. 멀티 스레드 앱을 작성하는 것은 다소 어려운 일이 될 수 있지만, 내가 쓴 첫 번째 Python 앱은 Mutlti-Shreaded Page Scraper였습니다. 쉘 실행 함수 중 하나를 사용하여 PHP 페이지 내에서 Python 스크립트를 호출 할 수 있습니다.

예, PHP에서 할 수 있습니다. 그러나 PHP 외에도 대기열 관리자를 사용하는 것이 좋습니다. 전략은 다음과 같습니다.

  1. 큰 작업을 작은 작업으로 나누십시오. 귀하의 경우 각 작업은 단일 페이지를로드 할 수 있습니다.

  2. 각 작은 작업을 대기열로 보냅니다.

  3. 대기열 노동자를 어딘가에 운영하십시오.

이 전략을 사용하는 것은 다음과 같은 장점이 있습니다.

  1. 장기적인 작업의 경우 실행 중에 치명적인 문제가 발생할 경우 회복 할 수 있습니다. 처음부터 시작할 필요가 없습니다.

  2. 작업을 순차적으로 실행할 필요가 없으면 여러 작업자를 실행하여 작업을 동시에 실행할 수 있습니다.

다양한 옵션이 있습니다 (이것은 단지 몇 안되는 것) :

  1. RabbitMQ (https://www.rabbitmq.com/tutorials/tutorial-one-php.html)
  2. Zeromq (http://zeromq.org/bindings:php)
  3. Laravel 프레임 워크를 사용하는 경우 대기열이 내장되어 있습니다 (https://laravel.com/docs/5.4/queues), aws ses, redis, beanstalkd의 드라이버와 함께

PHP는 최상의 도구 일 수도 있고 아닐 수도 있지만 사용 방법을 알고 있으며 나머지 응용 프로그램은이를 사용하여 작성됩니다. PHP가 "충분히 좋은"사실과 결합 된이 두 가지 특성은 Perl, Ruby 또는 Python 대신에 그것을 사용하는 데 꽤 강한 사례를 만듭니다.

목표가 다른 언어를 배우는 것이라면 하나를 골라 사용하십시오. 당신이 언급 한 모든 언어는 일을 할 것입니다. 나는 Perl을 좋아하지만 당신이 좋아하는 것은 다를 수 있습니다.

Symcbean은 그의 링크에서 배경 프로세스를 관리하는 방법에 대한 좋은 조언을 가지고 있습니다.

요컨대, 긴 비트를 처리하기 위해 CLI PHP 스크립트를 작성하십시오. 어떤 식 으로든 상태를보고하는지 확인하십시오. AJAX 또는 기존 메소드를 사용하여 상태 업데이트를 처리하기 위해 PHP 페이지를 작성하십시오. 킥오프 스크립트는 자체 세션에서 실행되는 프로세스를 시작하고 프로세스가 진행되고 있음을 확인합니다.

행운을 빕니다.

나는 이것이 백그라운드 과정에서 실행되어야한다는 답변에 동의합니다. 그러나 사용자가 작업이 수행되고 있음을 알 수 있도록 상태에 대해보고하는 것이 중요합니다.

PHP 요청을 받으려면 프로세스를 시작할 때 데이터베이스에 고유 식별자가있는 작업의 표현을 저장할 수 있습니다. 그런 다음 화면 스크래핑 프로세스를 시작하여 고유 식별자를 전달하십시오. 작업이 시작되었고 새 작업 ID가 포함 된 지정된 URL을 확인하여 최신 상태를 얻으려면 iPhone 앱에 다시보고하십시오. iPhone 애플리케이션은 이제이 URL을 폴링 할 수 있습니다 (또는 "긴 설문 조사"). 그 동안 배경 프로세스는 완료 백분율, 현재 단계 또는 원하는 다른 상태 표시기로 작업 할 때 작업의 데이터베이스 표현을 업데이트합니다. 그리고 완료되면 완성 된 깃발을 설정합니다.

XHR (AJAX) 요청으로 보낼 수 있습니다. 클라이언트는 일반적으로 일반 HTTP 요청과 달리 XHR에 대한 시간 초과가 없습니다.

나는 이것이 꽤 오래된 질문이지만 그것을 촬영하고 싶습니다. 이 스크립트는 초기 킥오프 콜을 모두 해결하여 빠르게 마무리하고 무거운 짐을 작은 덩어리로 자르려고합니다. 이 솔루션을 테스트하지 않았습니다.

<?php
/**
 * crawler.php located at http://mysite.com/crawler.php
 */

// Make sure this script will keep on runing after we close the connection with
// it.
ignore_user_abort(TRUE);


function get_remote_sources_to_crawl() {
  // Do a database or a log file query here.

  $query_result = array (
    1 => 'http://exemple.com',
    2 => 'http://exemple1.com',
    3 => 'http://exemple2.com',
    4 => 'http://exemple3.com',
    // ... and so on.
  );

  // Returns the first one on the list.
  foreach ($query_result as $id => $url) {
    return $url;
  }
  return FALSE;
}

function update_remote_sources_to_crawl($id) {
  // Update my database or log file list so the $id record wont show up
  // on my next call to get_remote_sources_to_crawl()
}

$crawling_source = get_remote_sources_to_crawl();

if ($crawling_source) {


  // Run your scraping code on $crawling_source here.


  if ($your_scraping_has_finished) {
    // Update you database or log file.
    update_remote_sources_to_crawl($id);

    $ctx = stream_context_create(array(
      'http' => array(
        // I am not quite sure but I reckon the timeout set here actually
        // starts rolling after the connection to the remote server is made
        // limiting only how long the downloading of the remote content should take.
        // So as we are only interested to trigger this script again, 5 seconds 
        // should be plenty of time.
        'timeout' => 5,
      )
    ));

    // Open a new connection to this script and close it after 5 seconds in.
    file_get_contents('http://' . $_SERVER['HTTP_HOST'] . '/crawler.php', FALSE, $ctx);

    print 'The cronjob kick off has been initiated.';
  }
}
else {
  print 'Yay! The whole thing is done.';
}

저는 Symcbean과 약간 다른 솔루션을 제안하고 싶습니다. 주로 장기 실행 프로세스를 apache/www-data 사용자가 아닌 다른 사용자로 실행해야 한다는 추가 요구 사항이 있기 때문입니다.

cron을 사용하여 백그라운드 작업 테이블을 폴링하는 첫 번째 솔루션:

  • PHP 웹 페이지가 백그라운드 작업 테이블에 'SUBMITTED' 상태로 삽입됩니다.
  • cron은 다른 사용자를 사용하여 3분마다 한 번씩 실행되며 백그라운드 작업 테이블에서 'SUBMITTED' 행을 확인하는 PHP CLI 스크립트를 실행합니다.
  • PHP CLI는 행의 상태 열을 'PROCESSING'으로 업데이트하고 처리를 시작합니다. 완료되면 'COMPLETED'로 업데이트됩니다.

Linux inotify 기능을 사용하는 두 번째 솔루션:

  • PHP 웹 페이지는 사용자가 설정한 매개변수로 제어 파일을 업데이트하고 작업 ID도 제공합니다.
  • inotifywait를 실행하는 쉘 스크립트(www가 아닌 ​​사용자)는 제어 파일이 기록될 때까지 기다립니다.
  • 제어 파일이 작성된 후 close_write 이벤트가 발생하고 쉘 스크립트가 계속됩니다.
  • 쉘 스크립트는 PHP CLI를 실행하여 장기 실행 프로세스를 수행합니다.
  • PHP CLI는 작업 ID로 식별되는 로그 파일에 출력을 기록하거나 상태 테이블의 진행 상황을 업데이트합니다.
  • PHP 웹 페이지는 로그 파일(작업 ID 기반)을 폴링하여 장기 실행 프로세스의 진행 상황을 표시하거나 상태 테이블을 쿼리할 수도 있습니다.

내 게시물에서 몇 가지 추가 정보를 찾을 수 있습니다. http://inventorsparadox.blogspot.co.id/2016/01/long-running-process-in-linux-using-php.html

Perl, Double Fork () 및 부모 프로세스에서 분리 된 것과 비슷한 일을했습니다. 모든 HTTP 페치 작업은 포크 프로세스에서 수행해야합니다.

프록시를 사용하여 요청을 위임하십시오.

내가 항상 사용하는 것은 이러한 변형 중 하나입니다 (Linux의 다른 맛은 출력 처리와 일부 프로그램 출력에 대한 규칙이 다르기 때문에 :

변형 i@exec ( './ myscript.php 1>/dev/null 2>/dev/null &');

변형 II@exec ( 'php -f myscript.php 1>/dev/null 2>/dev/null &');

변형 III@exec ( 'nohup myscript.php 1>/dev/null 2>/dev/null &');

"nohup"을 설치할 수 있습니다. 예를 들어, FFMPEG 비디오 대화를 자동화 할 때 출력 인터페이스는 출력 스트림 1 & 2를 리디렉션하여 100% 처리되지 않았으므로 NOHUP을 사용하여 출력을 리디렉션했습니다.

긴 스크립트가있는 경우 페이지를 나누면 각 작업에 대한 입력 매개 변수의 도움으로 페이지를 나누십시오. (각 페이지는 스레드처럼 작동합니다) 즉, 페이지 1 lac product_keywords 긴 프로세스 루프가있는 경우 루프 대신 하나의 키워드에 대한 논리를 만들고이 키워드를 전달합니다. Magic 또는 Cornjobpage.php에서 (다음 예에서)

그리고 배경 작업자의 경우이 기술을 시도해야한다고 생각합니다. 모든 페이지가 좋아하는 페이지만큼 많은 페이지를 호출하는 데 도움이 될 것입니다. 각 페이지 응답을 비동기로 기다리지 않고 독립적으로 실행됩니다.

Cornjobpage.php // 메인 페이지

    <?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

많은 사람들이 언급했듯이 최선의 접근 방식은 아니지만 도움이 될 수 있습니다.

ignore_user_abort(1); // run script in background even if user closes browser
set_time_limit(1800); // run it for 30 minutes

// Long running script here

스크립트의 원하는 출력이 웹 페이지가 아닌 일부 처리 인 경우 원하는 솔루션은 스크립트를 쉘에서 실행하는 것입니다.

php my_script.php

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top