양식을 제출 한 후 백그라운드에서 PHP 스크립트를 어떻게 실행할 수 있습니까?
문제
제출시 기본 코드를 실행하여 제출 된 정보를 처리하고 알림 웹 사이트에 표시하기 위해 데이터베이스에 삽입하는 양식이 있습니다. 또한 이메일 및 SMS 메시지를 통해 이러한 알림을 받도록 등록한 사람들의 목록이 있습니다. 이 목록은 현재로서는 사소한 일이지만 (약 150 명만 입력) 전체 구독자 테이블을 순환하고 150 개 이상의 이메일을 발송하는 데 1 분 이상 걸리기 만하면됩니다. (이메일은 대량 이메일 정책으로 인해 이메일 서버의 시스템 관리자가 요청한대로 개별적으로 전송됩니다.)
이 시간 동안 알림을 게시 한 개인은 알림이 게시되고 있다는 긍정적 인 보강없이 거의 1 분 동안 양식의 마지막 페이지에 앉아있을 것입니다. 이것은 내가 이상적이지 않다고 생각하는 가능한 해결책을 가진 다른 잠재적 인 문제로 이어집니다.
첫째, 포스터는 서버가 지연되고 있다고 생각하고 '제출'버튼을 다시 클릭하면 스크립트가 다시 시작되거나 두 번 실행됩니다. JavaScript를 사용하여 버튼을 비활성화하고 텍스트를 'Processing ...'과 같은 텍스트로 바꾸면이 문제를 해결할 수 있지만 사용자가 스크립트 실행 시간 동안 페이지에 계속 머물러 있기 때문에 이상적이지 않습니다. (또한 JavaScript가 비활성화 된 경우에도이 문제가 여전히 존재합니다.)
둘째, 포스터는 양식을 제출 한 후 탭이나 브라우저를 너무 일찍 닫을 수 있습니다. 스크립트는 브라우저에 다시 쓰기를 시도 할 때까지 서버에서 계속 실행되지만 사용자가 도메인 내의 페이지를 탐색하면 (스크립트가 아직 실행중인 동안) 브라우저는 스크립트가 종료 될 때까지 페이지로드를 중단합니다. . (이것은 브라우저의 탭이나 창이 전체 브라우저 응용 프로그램이 아닌 닫혀있을 때만 발생합니다.) 그래도 이상적이지 않습니다.
(가능) 솔루션
스크립트의 "이메일"부분을 알림이 게시 된 후 호출 할 수있는 별도의 파일로 나누고 싶습니다. 처음에는 알림이 성공적으로 게시 된 후 확인 페이지에 게시 할 생각이었습니다. 그러나 사용자는이 스크립트가 실행되고 있다는 것을 알지 못하며 어떤 이상 현상도 눈에 띄지 않습니다. 이 스크립트는 실패 할 수 없습니다.
하지만이 스크립트를 백그라운드 프로세스로 실행할 수 있다면 어떨까요? 제 질문은 이것이다 : PHP 스크립트를 어떻게 실행하여 백그라운드 서비스로 트리거하고 사용자가 양식 수준에서 수행 한 작업과 완전히 독립적으로 실행할 수 있습니까?
편집 : 이것은 cron'ed 수 없습니다 . 양식이 제출되는 즉시 실행되어야합니다. 우선 순위가 높은 알림입니다. 또한 서버를 실행하는 시스템 관리자는 크론이 5 분 이상 자주 실행되는 것을 허용하지 않습니다.
몇 가지 실험을 수행 exec
하고 shell_exec
난 완벽하게 작동하는 솔루션을 발견했다! shell_exec
발생하거나 발생하지 않는 모든 알림 프로세스를 기록 할 수 있도록 사용 하기로 선택합니다 . ( shell_exec
문자열로 반환되며 exec
이것은를 사용 하는 것보다 쉬웠으며 출력을 변수에 할당 한 다음 쓸 파일을 여는 것보다 쉽습니다 .)
다음 줄을 사용하여 이메일 스크립트를 호출하고 있습니다.
shell_exec("/path/to/php /path/to/send_notifications.php '".$post_id."' 'alert' >> /path/to/alert_log/paging.log &");
&
@netcoder가 지적한대로 명령의 끝에있는를 확인 하는 것이 중요합니다 . 이 UNIX 명령은 백그라운드에서 프로세스를 실행합니다.
스크립트 경로 뒤에 작은 따옴표로 묶인 추가 변수는 스크립트 $_SERVER['argv']
내에서 호출 할 수있는 변수 로 설정됩니다 .
그런 다음 이메일 스크립트는를 사용하여 내 로그 파일에 >>
출력하고 다음과 같이 출력합니다.
[2011-01-07 11:01:26] Alert Notifications Sent for http://alerts.illinoisstate.edu/2049 (SCRIPT: 38.71 seconds)
[2011-01-07 11:01:34] CRITICAL ERROR: Alert Notifications NOT sent for http://alerts.illinoisstate.edu/2049 (SCRIPT: 23.12 seconds)
Linux / Unix 서버에서는 proc_open 을 사용하여 백그라운드에서 작업을 실행할 수 있습니다 .
$descriptorspec = array(
array('pipe', 'r'), // stdin
array('file', 'myfile.txt', 'a'), // stdout
array('pipe', 'w'), // stderr
);
$proc = proc_open('php email_script.php &', $descriptorspec, $pipes);
여기서 &
중요한 부분입니다. 원본 스크립트가 종료 된 경우에도 스크립트는 계속됩니다.
PHP가 exec("php script.php")
할 수 있습니다.
로부터 수동 :
프로그램이이 함수로 시작된 경우 백그라운드에서 계속 실행하려면 프로그램의 출력을 파일 또는 다른 출력 스트림으로 리디렉션해야합니다. 그렇게하지 않으면 프로그램 실행이 끝날 때까지 PHP가 중단됩니다.
따라서 출력을 로그 파일로 리디렉션하면 (어쨌든 좋은 아이디어) 호출 스크립트가 중단되지 않고 이메일 스크립트가 bg에서 실행됩니다.
그리고 스크립트에서 HTTP 요청을 만들고 응답을 무시하지 않는 이유는 무엇입니까?
http://php.net/manual/en/function.httprequest-send.php
스크립트에 대한 요청을하는 경우 웹 서버를 호출해야 백그라운드에서 실행되며 (기본 스크립트에서) 스크립트가 실행 중임을 사용자에게 알리는 메시지를 표시 할 수 있습니다.
모든 답변 중에서 누구도 호출시 남아있는 모든 출력을 브라우저에 플러시하고 Fastcgi 세션과 HTTP 연결을 닫으면서 스크립트가 백그라운드에서 실행되도록 하는 말도 안되게 쉬운 fastcgi_finish_request 함수를 고려하지 않았습니다 .
예 :
<?php
header('Content-Type: application/json');
echo json_encode(['ok' => true]);
fastcgi_finish_request(); // The user is now disconnected from the script
// do stuff with received data,
이건 어때요?
- 양식을 보유하는 PHP 스크립트는 플래그 또는 일부 값을 데이터베이스 또는 파일에 저장합니다.
- 두 번째 PHP 스크립트는이 값을 주기적으로 폴링하며, 설정된 경우 동기 방식으로 이메일 스크립트를 트리거합니다.
이 두 번째 PHP 스크립트는 cron으로 실행되도록 설정해야합니다.
쉬운 방법으로 이것을 할 수 없다는 것을 알고 있기 때문에 (fork exec 등 (창에서 작동하지 않음) 참조) 접근 방식을 되돌릴 수 있으며 브라우저의 배경을 사용하여 ajax로 양식을 게시하므로 게시물이 여전히 당신은 대기 시간이 없습니다.
이것은 긴 정교함을해야하는 경우에도 도움이 될 수 있습니다.
메일 보내기에 관해서는 항상 스풀러를 사용하는 것이 좋습니다. 큐를 스풀링하는 크론을 사용하는 것보다 요청을 수락하고이를 실제 MTA에 스풀링하거나 모두 DB에 넣는 로컬 및 빠른 smtp 서버 일 수 있습니다.
크론은 스풀러를 외부 URL로 호출하는 다른 시스템에있을 수 있습니다.
* * * * * wget -O /dev/null http://www.example.com/spooler.php
백그라운드에서 PHP 스크립트를 실행하는 더 간단한 방법은 다음과 같습니다.
php script.php >/dev/null &
스크립트는 백그라운드에서 실행되며 페이지도 작업 페이지에 더 빠르게 도달합니다.
백그라운드 크론 작업은 이에 대한 좋은 생각처럼 들립니다.
스크립트를 cron으로 실행하려면 머신에 대한 ssh 액세스 권한이 필요합니다.
$ php scriptname.php
그것을 실행합니다.
ssh를 통해 서버에 액세스 할 수 있고 자신의 스크립트를 실행할 수 있다면 php를 사용하여 간단한 fifo 서버를 만들 수 있습니다 (하지만을 posix
지원하여 php를 다시 컴파일해야합니다 fork
).
서버는 실제로 무엇이든 쓸 수 있으며 아마도 파이썬으로 쉽게 할 수 있습니다.
또는 가장 간단한 해결책은 HttpRequest를 보내고 반환 데이터를 읽지 않는 것이지만 서버는 처리를 완료하기 전에 스크립트를 삭제할 수 있습니다.
예제 서버 :
<?php
define('FIFO_PATH', '/home/user/input.queue');
define('FORK_COUNT', 10);
if(file_exists(FIFO_PATH)) {
die(FIFO_PATH . ' exists, please delete it and try again.' . "\n");
}
if(!file_exists(FIFO_PATH) && !posix_mkfifo(FIFO_PATH, 0666)){
die('Couldn\'t create the listening fifo.' . "\n");
}
$pids = array();
$fp = fopen(FIFO_PATH, 'r+');
for($i = 0; $i < FORK_COUNT; ++$i) {
$pids[$i] = pcntl_fork();
if(!$pids[$i]) {
echo "process(" . posix_getpid() . ", id=$i)\n";
while(true) {
$line = chop(fgets($fp));
if($line == 'quit' || $line === false) break;
echo "processing (" . posix_getpid() . ", id=$i) :: $line\n";
// $data = json_decode($line);
// processData($data);
}
exit();
}
}
fclose($fp);
foreach($pids as $pid){
pcntl_waitpid($pid, $status);
}
unlink(FIFO_PATH);
?>
클라이언트 예 :
<?php
define('FIFO_PATH', '/home/user/input.queue');
if(!file_exists(FIFO_PATH)) {
die(FIFO_PATH . ' doesn\'t exist, please make sure the fifo server is running.' . "\n");
}
function postToQueue($data) {
$fp = fopen(FIFO_PATH, 'w+');
stream_set_blocking($fp, false); //don't block
$data = json_encode($data) . "\n";
if(fwrite($fp, $data) != strlen($data)) {
echo "Couldn't the server might be dead or there's a bug somewhere\n";
}
fclose($fp);
}
$i = 1000;
while(--$i) {
postToQueue(array('xx'=>21, 'yy' => array(1,2,3)));
}
?>
* nix 플랫폼에서 실행 중이라고 가정 cron
하고 php
실행 파일을 사용 하십시오 .
편집하다:
이미 "cron없이 php 실행"을 요구하는 많은 질문이 있습니다. 다음은 하나입니다.
That said, the exec() answer above sounds very promising :)
If you're on Windows, research proc_open
or popen
...
But if we're on the same server "Linux" running cpanel then this is the right approach:
#!/usr/bin/php
<?php
$pid = shell_exec("nohup nice php -f
'path/to/your/script.php' /dev/null 2>&1 & echo $!");
While(exec("ps $pid"))
{ //you can also have a streamer here like fprintf,
// or fgets
}
?>
Don't use fork()
or curl
if you doubt you can handle them, it's just like abusing your server
Lastly, on the script.php
file which is called above, take note of this make sure you wrote:
<?php
ignore_user_abort(TRUE);
set_time_limit(0);
ob_start();
// <-- really optional but this is pure php
//Code to be tested on background
ob_flush(); flush();
//this two do the output process if you need some.
//then to make all the logic possible
str_repeat(" ",1500);
//.for progress bars or loading images
sleep(2); //standard limit
?>
for background worker i think you should try this technique it will help to call as many as pages you like all pages will run at once independently without waiting for each page response as asynchronous.
form_action_page.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.
//your form db insertion or other code goes here do what ever you want //above code will work as background job this line will direct hit before //above lines response
?>
<?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
//here do your background operations it will not halt main page
?>
PS:if you want to send url parameters as loop then follow this answer :https://stackoverflow.com/a/41225209/6295712
In my case I have 3 params, one of them is string (mensaje):
exec("C:\wamp\bin\php\php5.5.12\php.exe C:/test/N/trunk/api/v1/Process.php $idTest2 $idTest3 \"$mensaje\" >> c:/log.log &");
In my Process.php I have this code:
if (!isset($argv[1]) || !isset($argv[2]) || !isset($argv[3]))
{
die("Error.");
}
$idCurso = $argv[1];
$idDestino = $argv[2];
$mensaje = $argv[3];
This is works for me. tyr this
exec(“php asyn.php”.” > /dev/null 2>/dev/null &“);
'Programing' 카테고리의 다른 글
PowerShell 스크립트를 사용하여 EXE 파일 실행 (0) | 2020.08.24 |
---|---|
.gitignore 파일에 Django 마이그레이션 파일을 추가해야합니까? (0) | 2020.08.24 |
기본 생성자는 더 이상 VS2015에서 컴파일되지 않습니다. (0) | 2020.08.24 |
기본 생성자는 더 이상 VS2015에서 컴파일되지 않습니다. (0) | 2020.08.24 |
C ++에서 "new"를 사용하는 경우와 사용하지 않는 경우 (0) | 2020.08.24 |