2013-08-14 1 views
3

exec ('command args>/log/file &');을 사용하는 PHP 스크립트가 있습니다. 루프 내에서 동시에 실행되는 여러 하위 스크립트를 만들 수 있습니다. 기본적으로 상위 스크립트는 데이터베이스에서 사용자 정보를 가져와 병렬로 실행되는 하위 스크립트를 만든 다음 하위 스크립트는 단일 사용자에게 보낼 전자 메일을 만듭니다. 이것은 약 50,000 번 발생합니다.PHP에서 exec는 자동으로 많은 exec 명령을 호출 할 때 실패하지만 나중에 동일한 명령을 실행하면 나중에 작동합니다.

동시에 실행중인 프로세스를 50,000 개 생성하지 않으려면 현재 실행중인 프로세스를 추적하는 데이터베이스 테이블이 있고 새 프로세스를 만들기 전에 부모 프로세스는 현재 자식 수를 확인하고 현재 25 명의 자식이 활성 상태 인 경우 절전 모드가됩니다. 자식은 작업을 완료하면 테이블에서 행을 삭제하여 부모가 더 많은 자식을 만들 수있게합니다.

문제는 약 10 %의 exec 명령이 자동으로 실패하고 겉으로보기에는 아무 이유도없는 것입니다. 상위 스크립트를 다시 실행할 수 있습니다 (동일한 사용자를 두 번 이메일하지 않는 것이 현명합니다). 그리고 마지막으로 실패한 동일한 exec 명령을 사용하여 90 %의 시간이 다시 작동합니다. 스크립트를 5 ~ 6 번 연속 실행하면 모든 사람에게 이메일을 보냅니다.

임원 직후에 잠을 둠으로써 성공률을 약 95 %까지 높일 수 있습니다.

왜 같은 명령이 나중에 작동하면 exec가 실패할까요? 완료 될 때까지 스크립트를 반복해서 보관할 수는 있지만 exec 문제를 해결하는 편이 낫습니다.

일부 매우 단순화 된 예제 코드 :

부모 스크립트 :

do { 
    //get user, group, and supergroup information for users that haven't 
    //been emailed yet 
    foreach ($users as $userArray) { 
     $processId = insertIntoProcessQueue($userArray); 
     $cmd = 'sudo php -q ./childScript.php ' . cliArg($userArray) . ' ' . 
       cliArg($groupArray) . ' ' . cliArg($supergroupArray) 
       ' ' . $proccessId . ' > file.log &'; 
     exec($cmd); 
     do { 
      if (numChildren() >= 25) { 
       sleep(1); 
       $waiting = true; 
      } 
     } while ($waiting); 
    } 
    $incomplete = moreUsersToEmail() > 0 ? true : false; 
} while ($incomplete); 

function cliArg($array) { 
    return escapeshellarg(json_encode($arg)); 
} 

아이 스크립트 : 스크립트가 완료 될 때

ignore_user_abort(true); 
$user = json_decode($argv[1]); 
$group = json_decode($argv[2]); 
$supergroup = json_decode($argv[3]); 

print_r($user); 

$email = createEmail($user, $group, $supergroup); 

$email->sendEmail(); 
removeFromProcessQueue($argv[4]); 
flush(); 
exit; 

는에서 print_r은 로그 파일에 표시됩니다 I 절대 오류가 발생하지 않으므로 실패 이유에 대한 데이터를 얻을 수 없습니다. 이를 추가하기 위해 모든 사용자가 일관되게 실패하지 않으며 한 번에 한 명의 사용자 만 실행할 수 있으므로 모든 사람을 통해 스크립트를 실행하고 45,000 개의 오류를 잡아야합니다. 제대로 작동합니다. 그리고 부모와 자식은 부모를 넘어서서는 결코 의사 소통을 할 수 없으므로 자식이 실패 할 때 (부모로부터) 발견 할 수 없습니다 (그렇지 않으면 부모에게 재실행하는 대신 실패한 자식을 다시 시도하고 시작할 수 있습니다.).

편집 : 따라서 동적으로 생성 된 스크립트가 사용되며 (사용 이유를 묻지 않습니다.) 스크립트가 생겨난 프로세스를 병렬로 실행하면서 경쟁 조건을 만듭니다. 실패.

불행히도 시간을 낭비 해 주셔서 감사합니다.

+0

'exec '의 반환 값을 확인하려고 했습니까? – Dave

+0

(네트워크를 병목 현상으로 처리하기 때문에 루프를 건너 뛰어도 프로세스가 복잡해집니다.) – Dave

+0

호스트의 메일 시스템에 너무 많은 것을 넣었습니까? 한 번에? 스레드 수를 2와 같이 매우 정상적인 값으로 낮추면 사망률이 여전히 높습니까? –

답변

0

그래서 동적으로 생성 된 스크립트가 사용되며 (사용 이유를 묻지 않습니다.) 스크립트가 실패하게 된 프로세스를 병렬로 실행하면서 경쟁 조건을 생성합니다.

불행히도 시간을 낭비 해 주셔서 감사합니다.

0

방금 ​​the PHP docs for exec()을 보았고 두 번째 매개 변수가있는 참조로 배열을 전달할 수 있습니다.이 매개 변수는 exec의 출력으로 채워집니다. 이를 사용하여 a) 명령이 실패한 이유와 b) 명령이 실패하고 코드에 오류가 통합 될 때를 결정할 수 있습니다.

그래서 나는 변경 것 :

exec($cmd); 

같은 사람 : 나는 또한 한 단계 더 가서에 대한 제한을 설정할 것, 그래서이 무한 루프 잠재적 것을

function check_exec_results($results) 
{ 
    echo '<HR><PRE>',print_r($output,true),'</PRE><HR>'; //use this to figure out what output you're getting from the exec commands then remove when you've figured out a way to set $results_look_good below 

    $results_look_good = ?; //you will need to edit this yourself to actually do some kind of check 

    return $results_look_good;   
} 

$successful_exec = false; 
do 
{ 
    $exec_results = array(); 
    exec($cmd,$exec_results); 
    $successful_exec = check_exec_results($exec_results); 
} 
while (!$successful_exec); 

주 exec()가 각 사용자에 대해 호출 될 수있는 횟수.

관련 문제