Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung der asynchronen PHP-Hintergrundverarbeitung

Detaillierte Erläuterung der asynchronen PHP-Hintergrundverarbeitung

藏色散人
藏色散人nach vorne
2020-12-04 14:02:398004Durchsuche

Detaillierte Erläuterung der asynchronen PHP-Hintergrundverarbeitung

PHP Asynchrone Hintergrundverarbeitung

PHP ist als Hintergrundschnittstellenserver weit verbreitet, und in tatsächlichen Anwendungsszenarien ist häufig eine asynchrone Hintergrundverarbeitung erforderlich.

Empfehlung: „PHP-Video-Tutorial

PHP hat sicherlich den Vorteil, ein Backend-Server zu sein, aber wenn es darum geht, einige Ergebnisse zu verarbeiten, die dem Client egal sind, zeigt es seine Nachteile, keine asynchrone Ausführung Mechanismus.

Wenn wir beispielsweise die Leistung eines bestimmten Client-Zugriffs auf PHP aufzeichnen möchten (einschließlich Startzeit, Endzeit, Ergebnisstatus usw.), möchte der Client natürlich, dass die PHP-Verarbeitung frühzeitig zurückkehrt und die Ergebnisse erhält , aber wenn Sie eine herkömmliche Lösung installieren, muss der Client warten, bis PHP die Leistungsaufzeichnung abgeschlossen hat, bevor er die Ergebnisse erhält.

Das ist so, als würde man zur Bank gehen, um den aktuellen Kontostand zu überprüfen, und der Kassierer lief zu ihnen, machte eine Weile Ärger mit anderen Leuten und kam dann zurück und teilte Ihnen das gleiche Ergebnis mit.

Oft benötigen Sie also ein PHP, das asynchrone Vorgänge ausführen kann.

Wie implementiert man asynchrone Verarbeitung in PHP?

Eine der Lösungen besteht darin, mithilfe von PHP-Systemaufrufen einen neuen Prozess zu starten, um dies zu erreichen.

php stellt die Funktion fsockopen bereit. Die Funktion dieser Funktion besteht darin, eine Socket-Verbindung zum angegebenen Host zu initialisieren.

Natürlich können Sie es über stream_set_blocking() in den nicht blockierenden Modus konvertieren. Das ist der Schlüssel.

Die Idee ist also: Öffnen Sie einen nicht blockierenden Socket, um eine Verbindung zum lokalen Computer herzustellen, und der lokale Computer führt nach dem Empfang eine zeitaufwändige Verarbeitung durch.

Ein Verarbeitungscode wie dieser (Datei 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);
}

Hier ist usleep(1000) sehr wichtig, es kann sicherstellen, dass diese Anfrage gesendet werden kann.

Wir betrachten die Logik des Verarbeitungscodes (Datei album_write_friends_thread_record.php):

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

Wenn unser Server das fsockopen-Programm ausführt, wartet er tatsächlich nicht 20 Sekunden, bevor er zum Client zurückkehrt,

sondern nachdem er dies ausgegeben hat Auf Anfrage kehrt es zum Client zurück, zerstört den Prozess und überlässt die verbleibende Arbeit anderen Prozessen, um sie langsam zu erledigen. Dadurch wird die Asynchronität von PHP erkannt.

PHP 4 gängige Methoden der asynchronen Ausführung

Der Client und der Server kommunizieren über das HTTP-Protokoll, und der Server führt die Verarbeitung nach Erhalt der Anforderung durch und gibt das Verarbeitungsergebnis zurück.

Manchmal muss der Server zeitaufwändige Vorgänge ausführen, z. B. Download-Verarbeitung, Nachrichtenzustellung, E-Mail-Versand usw. Die Ergebnisse dieses Vorgangs müssen nicht an den Client zurückgegeben werden.

Da PHP jedoch synchron ausgeführt wird, muss der Client warten, bis der Dienst verarbeitet wird, bevor er mit dem nächsten Schritt fortfahren kann.

Daher eignen sich zeitaufwändige Vorgänge für die asynchrone Ausführung. Nachdem der Server die Anforderung erhalten hat, kehrt er zuerst zurück, nachdem er die vom Client benötigten Daten verarbeitet hat, und die verbleibenden zeitaufwändigen Vorgänge werden asynchron im Hintergrund ausgeführt Server.

Zu den häufig verwendeten Methoden der asynchronen PHP-Ausführung gehören die folgenden, die Sie entsprechend ihren jeweiligen Vor- und Nachteilen auswählen können:

1. Ajax-Anfrage

Die Clientseite verwendet AJAX-Technologie, um den Server anzufordern

$.get("doRequest.php", { name: "fdipzone"} );
<img src="doRequest.php?name=fdipzone">

Vorteile: Der einfachste und schnellste Weg besteht darin, einen AJAX-Aufruf in den an den Client zurückgegebenen HTML-Code einzubetten oder ein img-Tag mit src einzubetten, das auf das auszuführende zeitaufwändige Skript verweist.

Nachteile: Im Allgemeinen sollte Ajax nach onLoad ausgelöst werden. Das heißt, wenn der Benutzer auf die Seite klickt und sie dann schließt, wird unser Hintergrundskript nicht ausgelöst.

Wenn Sie das img-Tag verwenden, kann diese Methode nicht als asynchrone Ausführung im engeren Sinne bezeichnet werden. Der Browser des Benutzers wartet lange auf den Abschluss der Ausführung des PHP-Skripts, d. h. die Statusleiste des Browsers des Benutzers zeigt immer an, dass es noch geladen wird.

Natürlich können Sie auch andere Methoden mit ähnlichen Prinzipien verwenden, wie zum Beispiel Script-Tags usw.

2. popen()-Funktion

Diese Funktion öffnet eine Pipe, die auf den Prozess zeigt, der durch die Ausführung des angegebenen Befehls erzeugt wird.

Öffnet eine Pipe zu dem Prozess, der durch die Verzweigung der Ausführung des angegebenen Befehls entsteht.

Sie können es also aufrufen, aber seine Ausgabe ignorieren. Der Verwendungscode lautet wie folgt:

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

Vorteile: Es vermeidet die Mängel der ersten Methode und verfügt über eine schnelle Ausführungsgeschwindigkeit.

Nachteile: Diese Methode kann keinen anderen WebService über das HTTP-Protokoll anfordern und kann nur lokale Skriptdateien ausführen. Und es kann nur in eine Richtung geöffnet werden und kann nicht viele Parameter an das aufgerufene Skript übergeben. Und wenn die Anzahl der Besuche hoch ist, werden viele Prozesse generiert. Wenn Sie externe Ressourcen nutzen, müssen Sie selbst Rücksicht auf die Konkurrenz nehmen.

1) Kann nur lokal ausgeführt werden

2) Es können nicht viele Parameter übergeben werden

3) Bei hohem Datenverkehr werden viele Prozesse erstellt

3. Curl-Erweiterung

CURL ist Ein leistungsstarkes HTTP-Befehlszeilentool kann HTTP-Anforderungen wie POST/GET simulieren, dann Daten abrufen, extrahieren und auf der „Standardausgabe“ (stdout) anzeigen.

设置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 的继续往下执行,表明这一过程是异步的。

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der asynchronen PHP-Hintergrundverarbeitung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:mimvp. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen