天天看點

php實作計劃任務與持續程序執行個體 fsockopen

web伺服器執行一個php腳本,有時耗時很長才能傳回執行結果,後面的腳本需要等待很長一段時間才能繼續執行。如果想實作隻簡單觸發耗時腳本的執行而不等待執行結果就直接執行下一步操作,可以通過fscokopen函數來實作。

php支援socket程式設計,fscokopen函數傳回一個到遠端主機連接配接的句柄,可以像使用fopen傳回的句柄一樣,對它進行 fwrite、fgets、fread等操作。使用fsockopen連接配接到本地伺服器,觸發腳本執行,然後立即傳回,不等待腳本執行完成,即可實作異步 執行php的效果。

php實作計劃任務與持續程式執行個體 fsockopen

<?  

function triggerrequest($url, $post_data = array(), $cookie = array()){  

        $method = "get";  //通過post或者get傳遞一些參數給要觸發的腳本  

        $url_array = parse_url($url); //擷取url資訊  

        $port = isset($url_array['port'])? $url_array['port'] : 80;    

        $fp = fsockopen($url_array['host'], $port, $errno, $errstr, 30);  

        if (!$fp) {  

                return false;  

        }  

        $getpath = $url_array['path'] ."?". $url_array['query'];  

        if(!empty($post_data)){  

                $method = "post";  

        $header = $method . " " . $getpath;  

        $header .= " http/1.1\r\n";  

        $header .= "host: ". $url_array['host'] . "\r\n "; //http 1.1 host域不能省略  

        /*以下頭資訊域可以省略 

        $header .= "user-agent: mozilla/5.0 (windows; u; windows nt 5.1; en-us; rv:1.8.1.13) gecko/20080311 firefox/2.0.0.13 \r\n"; 

        $header .= "accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,q=0.5 \r\n"; 

        $header .= "accept-language: en-us,en;q=0.5 "; 

        $header .= "accept-encoding: gzip,deflate\r\n"; 

         */  

        $header .= "connection:close\r\n";  

        if(!empty($cookie)){  

                $_cookie = strval(null);  

                foreach($cookie as $k => $v){  

                        $_cookie .= $k."=".$v."; ";  

                }  

                $cookie_str =  "cookie: " . base64_encode($_cookie) ." \r\n"; //傳遞cookie  

                $header .= $cookie_str;  

                $_post = strval(null);  

                foreach($post_data as $k => $v){  

                        $_post .= $k."=".$v."&";  

                $post_str  = "content-type: application/x-www-form-urlencoded\r\n";   

                $post_str .= "content-length: ". strlen($_post) ." \r\n"; //post資料的長度  

                $post_str .= $_post."\r\n\r\n "; //傳遞post資料  

                $header .= $post_str;  

        fwrite($fp, $header);  

        //echo fread($fp, 1024); //伺服器傳回  

        fclose($fp);  

        return true;  

}     

 這樣就可以通過fsockopen()函數來觸發一個php腳本的執行,然後函數就會傳回。 接着執行下一步操作了。

現在存在一個問題:當用戶端斷開連接配接後,也就是triggerrequest發送請求後,立即關閉了連接配接,那麼可能會引起伺服器端正在執行的腳本退出

在 php 内部,系統維護着連接配接狀态,其狀态有三種可能的情況:

* 0 – normal(正常)

* 1 – aborted(異常退出)

* 2 – timeout(逾時)

當 php 腳本正常地運作 normal 狀态時,連接配接為有效。當用戶端中斷連接配接時,aborted 狀态的标記将會被打開。遠端用戶端連接配接的中斷通常是由使用者點選 stop 按鈕導緻的。當連接配接時間超過 php 的時限(參閱 set_time_limit() 函數)時,timeout 狀态的标記将被打開。

可以決定腳本是否需要在用戶端中斷連接配接時退出。有時候讓腳本完整地運作會帶來很多友善,即使沒有遠端浏覽器接受腳本的輸出。預設的情況是當遠端用戶端 連接配接 中斷時腳本将會退出。該處理過程可由 php.ini 的 ignore_user_abort 或由 apache .conf 設定中對應的"php_value ignore_user_abort"以及 ignore_user_abort() 函數來控制。如果沒有告訴 php忽略使用者的中斷,腳本将會被中斷,除非通過 register_shutdown_function() ,可以讓我們設定一個當執行關閉時可以被調用的另一個函數.也就是說當我們的腳本執行完成或意外死掉導緻php執行即将關閉時,我們的這個函數将會 被調用,當遠端使用者點選 stop 按鈕後,腳本再次嘗試輸出資料時,php 将會檢測到連接配接已被中斷,并調用關閉觸發函數。

腳本也有可能被内置的腳本計時器中斷。預設的逾時限制為 30 秒。這個值可以通過設定 php.ini 的 max_execution_time 或 apache .conf 設定中對應的"php_value max_execution_time"參數或者 set_time_limit() 函數來更改。當計數器逾時的時候,腳本将會類似于以上連接配接中斷的情況退出,先前被注冊過的關閉觸發函數也将在這時被執行。在該關閉觸發函數中,可以通過調 用 connection_status() 函數來檢查逾時是否導緻關閉觸發函數被調用。如果逾時導緻了關閉觸發函數的調用,該函數将傳回 2。

需要注意的一點是 aborted 和 timeout 狀态可以同時有效。這在告訴 php 忽略使用者的退出操作時是可能的。php 将仍然注意使用者已經中斷了連接配接但腳本仍然在運作的情況。如果到了運作的時間限制,腳本将被退出,設定過的關閉觸發函數也将被執行。在這時會發現函數 connection_status() 傳回 3。

是以還在要觸發的腳本中指明:

php實作計劃任務與持續程式執行個體 fsockopen

<?php  

  ignore_user_abort(true); //如果用戶端斷開連接配接,不會引起腳本abort  

  set_time_limit(0); //取消腳本執行延時上限  

或使用: 

php實作計劃任務與持續程式執行個體 fsockopen

  register_shutdown_function(callback fuction[, parameters]); //注冊腳本退出時執行的函數