PHP 비동기 백그라운드 처리에 대한 자세한 설명

藏色散人
풀어 주다: 2023-04-09 16:54:02
앞으로
7988명이 탐색했습니다.

PHP 비동기 백그라운드 처리에 대한 자세한 설명

PHP 비동기 백그라운드 처리

PHP는 백그라운드 인터페이스 서버로 매우 보편화되었으며, 실제 애플리케이션 시나리오에서는 비동기 백그라운드 처리가 필요한 경우가 많습니다.

추천: "PHP 비디오 튜토리얼"

PHP는 확실히 백엔드 서버라는 장점이 있지만 클라이언트가 신경 쓰지 않는 일부 결과를 처리하는 경우 단점을 보여줍니다. 비동기 실행이 없습니다. 기구.

예를 들어, 특정 클라이언트의 PHP 액세스 성능(시작 시간, 종료 시간, 결과 상태 등 포함)을 기록하려는 경우 클라이언트는 물론 PHP 처리가 일찍 반환되어 결과를 얻기를 원합니다. 그러나 기존 솔루션을 설치하는 경우 클라이언트는 결과를 얻기 전에 PHP가 성능 기록을 완료할 때까지 기다려야 합니다.

잔고를 확인하러 은행에 갔는데, 창구 직원이 달려와서 한동안 다른 사람들과 소란을 피우다가 돌아와서 같은 결과를 알려주는 것과 같습니다.

그래서 비동기 작업을 수행할 수 있는 PHP가 필요한 경우가 많습니다.

PHP에서 비동기 처리를 구현하는 방법은 무엇입니까?

해결책 중 하나는 PHP 시스템 호출을 사용하여 이를 달성하기 위한 새로운 프로세스를 시작하는 것입니다.

php는 fsockopen 기능을 제공합니다. 이 기능의 기능은 지정된 호스트에 대한 소켓 연결을 초기화하는 것입니다. 기본적으로 소켓 연결은 차단 모드에서 열립니다.

물론 stream_set_blocking()을 통해 비차단 모드로 변환할 수 있습니다. 이것이 핵심입니다.

그래서 아이디어는: 비차단 소켓을 열어 로컬 컴퓨터에 연결하면 로컬 컴퓨터가 이를 수신한 후 시간이 많이 걸리는 처리를 수행한다는 것입니다.

다음과 같은 처리 코드(posttest.php 파일):

$fp = fsockopen($php_Path,80);
if (!$fp) {
    LMLog::error("fsockopen:err" );
} else {
    $out = "GET /album/action/album_write_friends_thread_record.php?key=&u=   HTTP/1.1\r\n";
    $out .= "Host: ".$php_Path."\r\n";
    $out .= "Connection: Close\r\n\r\n";
    stream_set_blocking($fp,true);
    stream_set_timeout($fp,1);
    fwrite($fp, $out);
    usleep(1000);
    fclose($fp);
}
로그인 후 복사

여기서 usleep(1000)은 매우 중요합니다. 이 코드를 사용하면 이 요청을 보낼 수 있습니다.

처리 코드 로직(file album_write_friends_thread_record.php)을 살펴보겠습니다.

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2016-09-23
 * Time: 09:26
 */
/**
 * 客户端调用服务器接口页面
 * user: guwen
 */
sleep(20);// 睡眠20s
?>
로그인 후 복사

실제로 우리 서버가 fsockopen 프로그램을 실행할 때 클라이언트로 돌아오기 전에 20초를 기다리지 않습니다.

하지만 이것을 발행한 후에는 요청을 받으면 클라이언트로 돌아가서 프로세스를 파괴하고 남은 작업을 다른 프로세스에 맡겨서 천천히 수행하게 합니다.

PHP 4가지 일반적인 비동기 실행 방법

클라이언트와 서버는 HTTP 프로토콜을 통해 통신하고, 서버는 요청을 받은 후 처리를 수행하고 처리 결과를 반환합니다.

때때로 서버는 다운로드 처리, 메시지 전달, 이메일 전송 등과 같이 시간이 많이 걸리는 작업을 수행해야 합니다. 이 작업의 결과는 클라이언트에 반환될 필요가 없습니다.

하지만 PHP는 동기적으로 실행되기 때문에 클라이언트는 다음 단계로 진행하기 전에 서비스가 처리될 때까지 기다려야 합니다.

따라서 시간이 많이 걸리는 작업은 비동기식 실행에 적합합니다. 서버가 요청을 받은 후 클라이언트가 요구하는 데이터를 처리한 후 먼저 반환하고 나머지 시간이 많이 걸리는 작업은 백그라운드에서 비동기식으로 실행됩니다. 섬기는 사람.

일반적으로 사용되는 PHP 비동기 실행 방법은 다음과 같으며 각각의 장점과 단점에 따라 선택할 수 있습니다.

1 ajax 요청

클라이언트 페이지는 AJAX 기술을 사용하여 서버를 요청합니다

$.get("doRequest.php", { name: "fdipzone"} );
<img src="doRequest.php?name=fdipzone">
로그인 후 복사

장점: 가장 간단하고 빠른 방법은 클라이언트에 반환된 HTML 코드에 AJAX 호출을 포함하거나 실행될 시간이 많이 걸리는 스크립트를 가리키는 src가 포함된 img 태그를 포함하는 것입니다.

단점: 일반적으로 Ajax는 onLoad 후에 실행되어야 합니다. 즉, 사용자가 페이지를 클릭한 다음 닫으면 백그라운드 스크립트가 실행되지 않습니다.

img 태그를 사용하는 경우 이 메서드는 엄격한 의미에서 비동기 실행이라고 할 수 없습니다. 사용자 브라우저는 PHP 스크립트 실행이 완료될 때까지 오랜 시간 동안 기다립니다. 즉, 사용자 브라우저의 상태 표시줄에는 항상 로드 중이라는 메시지가 표시됩니다.

물론 스크립트 태그 등 유사한 원리를 가진 다른 방법을 사용할 수도 있습니다.

2.popen() 함수

이 함수는 주어진 명령 명령을 실행하여 생성된 프로세스를 가리키는 파이프를 엽니다.

주어진 명령 명령의 실행을 분기하여 생성된 프로세스에 대한 파이프를 엽니다.

그래서 호출할 수 있지만 출력은 무시하세요. 사용 코드는 다음과 같습니다.

// popen — 打开进程文件指针  
resource popen ( string $command , string $mode )
pclose(popen(&#39;php /home/fdipzone/doRequest.php &&#39;, &#39;r&#39;));
로그인 후 복사

장점: 첫 번째 방법의 단점을 피하고 실행 속도가 빠릅니다.

단점: 이 방법은 HTTP 프로토콜을 통해 다른 WebService를 요청할 수 없으며 로컬 스크립트 파일만 실행할 수 있습니다. 그리고 한 방향으로만 열 수 있으며 호출된 스크립트에 많은 수의 매개변수를 전달할 수 없습니다. 그리고 방문 횟수가 많으면 많은 수의 프로세스가 생성됩니다. 외부 리소스를 사용하는 경우 경쟁을 직접 고려해야 합니다.

1) 로컬에서만 실행 가능

2) 많은 수의 매개변수를 전달할 수 없음

3) 트래픽이 높을 때 많은 프로세스가 생성됩니다

3.curl 확장

CURL은 강력한 HTTP 명령줄 도구는 POST/GET과 같은 HTTP 요청을 시뮬레이션한 다음 데이터를 얻고 추출하여 "표준 출력"(stdout)에 표시할 수 있습니다.

设置curl的超时时间 CURLOPT_TIMEOUT 为1 (最小为1),因此客户端需要等待1秒

代码如下:

<?php 
    $ch = curl_init(); 
    $curl_opt = array( 
      CURLOPT_URL, &#39;http://www.example.com/doRequest.php&#39;
      CURLOPT_RETURNTRANSFER,1, 
      CURLOPT_TIMEOUT,1 
    ); 
    curl_setopt_array($ch, $curl_opt); 
    curl_exec($ch); 
    curl_close($ch); 
?>
로그인 후 복사

缺点:如你问题中描述的一样,由于使用CURL需要设置CUROPT_TIMEOUT为1(最小为1,郁闷)。也就是说,客户端至少必须等待1秒钟。

4. fscokopen()函数

fsockopen是最好的,缺点是需要自己拼接header部分。

<?php 
    $url = &#39;http://www.example.com/doRequest.php&#39;; 
    $param = array( 
      &#39;name&#39;=>&#39;fdipzone&#39;, 
      &#39;gender&#39;=>&#39;male&#39;, 
      &#39;age&#39;=>30 
    ); 
         
    doRequest($url, $param); 
         
    function doRequest($url, $param=array()){ 
        $urlinfo = parse_url($url); 
 
        $host = $urlinfo[&#39;host&#39;]; 
        $path = $urlinfo[&#39;path&#39;]; 
        $query = isset($param)? http_build_query($param) : &#39;&#39;; 
 
        $port = 80; 
        $errno = 0; 
        $errstr = &#39;&#39;; 
        $timeout = 10; 
 
        $fp = fsockopen($host, $port, $errno, $errstr, $timeout); 
 
        $out = "POST ".$path." HTTP/1.1\r\n"; 
        $out .= "host:".$host."\r\n"; 
        $out .= "content-length:".strlen($query)."\r\n"; 
        $out .= "content-type:application/x-www-form-urlencoded\r\n"; 
        $out .= "connection:close\r\n\r\n"; 
        $out .= $query; 
 
        fputs($fp, $out); 
        fclose($fp); 
    } 
?>
로그인 후 복사

注意:当执行过程中,客户端连接断开或连接超时,都会有可能造成执行不完整,因此需要加上

ignore_user_abort(true); // 忽略客户端断开 
set_time_limit(0);    // 设置执行不超时
로그인 후 복사

fsockopen支持socket编程,可以使用fsockopen实现邮件发送等socket程序等等,使用fcockopen需要自己手动拼接出header部分

可以参考: http://cn.php.net/fsockopen/

使用示例如下:

$fp = fsockopen("www.34ways.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "GET /index.php  / HTTP/1.1\r\n";
    $out .= "Host: www.34ways.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
   
    fwrite($fp, $out);
    /*忽略执行结果
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }*/
    fclose($fp);
}
로그인 후 복사

所以总结来说,fscokopen()函数应该可以满足您的要求。可以尝试一下。

fscokopen的问题和popen 一样,并发非常多时会产生很多子进程,当达到apache的连接限制数时,就会挂掉,我问题已经说了这种情况。

PHP 本身没有多线程的东西,但可以曲线的办法来造就出同样的效果,比如多进程的方式来达到异步调用,只限于命令模式。还有一种更简单的方式,可用于 Web 程序中,那就是用fsockopen()、fputs() 来请求一个 URL 而无需等待返回,如果你在那个被请求的页面中做些事情就相当于异步了。  

关键代码如下:

$fp=fsockopen(&#39;localhost&#39;,80,&$errno,&$errstr,5);
if(!$fp){
    echo "$errstr ($errno)<br />\n";
}
fputs($fp,"GET another_page.php?flag=1\r\n");
fclose($fp);
로그인 후 복사

上面的代码向页面 another_page.php 发送完请求就不管了,用不着等待请求页面的响应数据,利用这一点就可以在被请求的页面 another_page.php 中异步的做些事情了。

比如,一个很切实的应用,某个 Blog 在每 Post 了一篇新日志后需要给所有它的订阅者发个邮件通知。如果按照通常的方式就是:

日志写完 -> 点提交按钮 -> 日志插入到数据库 -> 发送邮件通知 ->

告知撰写者发布成功

那么作者在点提交按钮到看到成功提示之间可能会等待很常时间,基本是在等邮件发送的过程,比如连接邮件服务异常、或器缓慢或是订阅者太多。而实际上是不管邮件发送成功与否,保证日志保存成功基本可接受的,所以等待邮件发送的过程是很不经济的,这个过程可异步来执行,并且邮件发送的结果不太关心或以日志形式记录备查。

改进后的流程就是:

日志写完 -> 点提交按钮 -> 日志插入到数据库 --->

告知撰写者发布成功

└ 发送邮件通知 -> [记下日志]

用个实际的程序来测试一下,有两个 php,分别是 write.php 和 sendmail.php,在 sendmail.php 用 sleep(seconds) 来模拟程序执行使用时间。

write.php,执行耗时 1 秒

<?php 
    function asyn_sendmail() {
        $fp=fsockopen(&#39;localhost&#39;,80,&$errno,&$errstr,5);
        if(!$fp){
            echo "$errstr ($errno)<br />\n";
        }
        sleep(1);
        fputs($fp,"GET /sendmail.php?param=1\r\n"); #请求的资源 URL 一定要写对
        fclose($fp);
    } 
      
    echo time().&#39;<br>&#39;;
    echo &#39;call asyn_sendmail<br>&#39;;
    asyn_sendmail();
    echo time().&#39;<br>&#39;;
    ?>
로그인 후 복사

sendmail.php,执行耗时 10 秒

<?php
    //sendmail();
    //sleep 10 seconds
    sleep(10);
    fopen(&#39;C:\&#39;.time(),&#39;w&#39;);
?>
로그인 후 복사

通过页面访问 write.php,页面输出:

1272472697 call asyn_sendmail
1272472698
로그인 후 복사

并且在 C:\ 生成文件:

1272472708
로그인 후 복사

从上面的结果可知 sendmail.php 花费至少 10 秒,但不会阻塞到 write.php 的继续往下执行,表明这一过程是异步的。

위 내용은 PHP 비동기 백그라운드 처리에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
php
원천:mimvp
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!