ホームページ >バックエンド開発 >PHPチュートリアル >PHPの非同期バックグラウンド処理の詳細説明

PHPの非同期バックグラウンド処理の詳細説明

藏色散人
藏色散人転載
2020-12-04 14:02:398071ブラウズ

PHPの非同期バックグラウンド処理の詳細説明

PHP の非同期バックグラウンド処理

PHP はバックグラウンド インターフェイス サーバーとして非常に一般的であり、実際のアプリケーション シナリオでは非同期バックグラウンド処理が必要になることがよくあります。

推奨: 「PHP ビデオ チュートリアル

PHP には確かにバックエンド サーバーとしての利点がありますが、一部のクライアントを処理するのは簡単ではありません。結果を気にすると、その欠点が明らかになり、非同期実行メカニズムは存在しません。

たとえば、特定のクライアントの PHP へのアクセスに関するパフォーマンス記録 (開始時刻、終了時刻、結果ステータスなどを含む) を記録したい場合、当然のことながら、クライアントは PHP の現在のパフォーマンスを記録したいと考えます。処理は早期に戻って結果を取得できますが、従来のソリューションがインストールされている場合、クライアントは結果を取得する前に PHP がパフォーマンスの記録を完了するまで待つ必要があります。

これは、現在の残高を確認するために銀行に行ったときに、窓口係が駆け寄って他の人としばらく議論した後、戻ってきて同じ結果を告げたのと同じです。

したがって、多くの場合、非同期操作を実行できる PHP が必要になります。

PHP はどのように非同期処理を実装するのでしょうか?

解決策の 1 つは、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) は非常に重要であり、このリクエストを確実に送信できるようになります。

処理コード ロジック (ファイル 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 の非同期性が実現されます。

PHP 非同期実行の 4 つの一般的な方法

クライアントとサーバーは HTTP プロトコルを通じて通信し、クライアントはリクエストの場合、サーバーはリクエストを受け取った後に処理を実行し、処理結果を返します。

サーバーは、ダウンロードの処理、メッセージ配信、電子メール送信など、時間のかかる操作を実行する必要がある場合があります。この操作の結果をクライアントに返す必要はありません。

ただし、PHP は同期的に実行されるため、クライアントは次のステップに進む前にサービスが処理されるまで待つ必要があります。

したがって、時間のかかる操作は非同期実行に適しており、サーバーはリクエストを受信した後、クライアントが必要とするデータを処理して最初に戻り、残りの時間のかかる操作は非同期で実行されます。サーバーの背景。

PHP の非同期実行でよく使われる方法には次のようなものがありますが、それぞれの長所と短所に応じて選択できます。

##クライアント ページは AJAX テクノロジを使用してサーバーをリクエストします

$.get("doRequest.php", { name: "fdipzone"} );
<img src="doRequest.php?name=fdipzone">
利点: 最も簡単かつ最速の方法は、クライアントに返される HTML コードに AJAX 呼び出しを埋め込むことです。または、 img タグを埋め込み、 src は実行される時間のかかるスクリプトを指します。

欠点: 一般に、Ajax は onLoad の後にトリガーされる必要があります。つまり、ユーザーがページをクリックして閉じた場合、バックグラウンド スクリプトはトリガーされません。

imgタグを使用した場合、このメソッドは厳密な意味での非同期実行とは言えません。ユーザーのブラウザは、php スクリプトの実行が完了するまで長時間待機します。つまり、ユーザーのブラウザのステータス バーには、ロード中であることが常に表示されます。

もちろん、スクリプト タグなど、同様の原理を持つ他のメソッドを使用することもできます。

2.popen() 関数

この関数は、Produced から派生したプロセスを指すパイプを開きます。特定のコマンドの実行によって。 指定されたコマンドの実行をフォークすることによって生成されたプロセスを指すパイプを開きます。

したがって、これを呼び出すことはできますが、その出力は無視できます。使用コードは次のとおりです。

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

利点: 最初の方法の欠点が回避され、実行速度が速くなります。

欠点: この方法では、HTTP プロトコルを通じて別の Web サービスをリクエストできず、ローカル スクリプト ファイルのみを実行できます。また、一方向にのみ開くことができ、呼び出されたスクリプトに多数のパラメータを渡すことはできません。また、アクセス数が多ければ、大量のプロセスが生成されます。外部リソースを使用する場合は、競合について自分で考慮する必要があります。

1) ローカルマシンでのみ実行できます

2) 大量のパラメータを渡すことはできません

3) アクセス数が多いと多くのプロセスが作成されますhigh

3.curl 拡張機能

CURL は、次のような HTTP リクエストをシミュレートできる強力な HTTP コマンド ライン ツールです。 POST/GET を実行し、「標準出力」(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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はmimvpで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。