溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

PHP異步執行的4種常用方式是什么

發布時間:2020-12-07 10:19:59 來源:億速云 閱讀:239 作者:小新 欄目:編程語言

這篇文章將為大家詳細講解有關PHP異步執行的4種常用方式是什么,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

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) 非常關鍵,它能保證這個請求能發出去。

我們在來看處理的代碼邏輯(文件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 那段程序時,就不會再等20s之后才能返回給客戶端,

而是發出這個請求之后,即返回客戶端,銷毀進程,而把剩余的工作交由其他進程慢慢做去,這就實現了php的異步。

PHP 異步執行的4種常用方式

客戶端與服務器端是通過HTTP協議進行連接通訊,客戶端發起請求,服務器端接收到請求后執行處理,并返回處理結果。

有時服務器需要執行很耗時的操作,如處理下載、消息下發、郵件發送等,這個操作的結果并不需要返回給客戶端。

但因為php是同步執行的,所以客戶端需要等待服務處理完才可以進行下一步。

因此,對于耗時的操作適合異步執行,服務器接收到請求后,處理完客戶端需要的數據就先返回,剩余耗時的操作再異步在服務器后臺執行。

PHP異步執行的常用方式常見的有以下幾種,可以根據各自優缺點進行選擇:

1. ajax 請求

客戶端頁面采用AJAX技術請求服務器

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

優點:最簡單,也最快,就是在返回給客戶端的HTML代碼中,嵌入AJAX調用,或者,嵌入一個img標簽,src指向要執行的耗時腳本。

缺點:一般來說Ajax都應該在onLoad以后觸發,也就是說,用戶點開頁面后,就關閉,那就不會觸發我們的后臺腳本了。

而使用img標簽的話,這種方式不能稱為嚴格意義上的異步執行。用戶瀏覽器會長時間等待php腳本的執行完成,也就是用戶瀏覽器的狀態欄一直顯示還在load。

當然,還可以使用其他的類似原理的方法,比如script標簽等等。

2. popen()函數

該函數打開一個指向進程的管道,該進程由派生給定的 command 命令執行而產生。

打開一個指向進程的管道,該進程由派生給定的 command 命令執行而產生。

所以可以通過調用它,但忽略它的輸出。使用代碼如下:

// popen — 打開進程文件指針  
resource popen ( string $command , string $mode )
pclose(popen('php /home/fdipzone/doRequest.php &', 'r'));

優點:避免了第一個方法的缺點,并且執行速度快。

缺點:這種方法不能通過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, 'http://www.example.com/doRequest.php'
      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 = 'http://www.example.com/doRequest.php'; 
    $param = array( 
      'name'=>'fdipzone', 
      'gender'=>'male', 
      'age'=>30 
    ); 
         
    doRequest($url, $param); 
         
    function doRequest($url, $param=array()){ 
        $urlinfo = parse_url($url); 
 
        $host = $urlinfo['host']; 
        $path = $urlinfo['path']; 
        $query = isset($param)? http_build_query($param) : ''; 
 
        $port = 80; 
        $errno = 0; 
        $errstr = ''; 
        $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()函數應該可以滿足您的要求??梢試L試一下。

fscokopen的問題和popen 一樣,并發非常多時會產生很多子進程,當達到apache的連接限制數時,就會掛掉,我問題已經說了這種情況。

PHP 本身沒有多線程的東西,但可以曲線的辦法來造就出同樣的效果,比如多進程的方式來達到異步調用,只限于命令模式。還有一種更簡單的方式,可用于 Web 程序中,那就是用fsockopen()、fputs() 來請求一個 URL 而無需等待返回,如果你在那個被請求的頁面中做些事情就相當于異步了?! ?/p>

關鍵代碼如下:

$fp=fsockopen('localhost',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('localhost',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().'<br>';
    echo 'call asyn_sendmail<br>';
    asyn_sendmail();
    echo time().'<br>';
    ?>

sendmail.php,執行耗時 10 秒

<?php
    //sendmail();
    //sleep 10 seconds
    sleep(10);
    fopen('C:\'.time(),'w');
?>

通過頁面訪問 write.php,頁面輸出:

1272472697 call asyn_sendmail
1272472698

并且在 C:\ 生成文件:

1272472708

從上面的結果可知 sendmail.php 花費至少 10 秒,但不會阻塞到 write.php 的繼續往下執行,表明這一過程是異步的。

關于PHP異步執行的4種常用方式是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女