首頁 > 後端開發 > php教程 > 批次之家 PHP 中的批次的實現

批次之家 PHP 中的批次的實現

WBOY
發布: 2016-07-29 08:37:03
原創
955 人瀏覽過

如果 Web 應用程式中的一個特性需要超過 1 秒或 2 秒才能完成,那麼該怎麼辦?需要某種離線處理解決方案。學習幾種對 PHP 應用程式中長時間運作的作業進行離線服務的方法。
大型的連鎖店有一個大問題。每天,在每家商店會發生數千次交易。公司執行官希望對這些數據進行挖掘。哪些產品賣得好?哪些不好?有機產品在哪裡賣得好?冰淇淋的銷售情況怎麼樣? 
為了捕捉這些數據,組織必須將所有事務性數據裝載進一個數據模型,以便更適合產生公司所需的報告類型。但是,這很花費時間,而且隨著連鎖規模的增長,處理一天的數據可能要花費一天以上的時間。因此,這是個大問題。
現在,您的 Web 應用程式可能不需要處理這麼多數據,但是任何網站的處理時間都有可能超過客戶願意等待的時間。一般來說,顧客願意等待的時間是 200 毫秒,如果超過這個時間,顧客就會覺得過程 「緩慢」。這個數字是基於桌面應用程序,而 Web 則讓我們更有耐心了。但無論如何,不應該讓客戶等待的時間超過幾秒。所以,要用一些策略來處理 PHP 中的批次作業。
分散的方式與 cron
在 UNIX® 機器上,執行批次的核心程序是 cron 守護程式。這個守護程序讀取一個設定文件,這個文件會告訴它要運行哪些命令列以及運行的頻率。然後,這個守護程式就按照配置執行它們。在遇到錯誤時,它甚至能夠向指定的電子郵件地址發送錯誤輸出,從而幫助對問題進行偵錯。
我知道有些工程師強烈主張使用執行緒技術。 「線程!線程才是進行後台處理的真正方法。cron 守護程序太過時了。」 
我不這麼認為。
這兩種方法我都用過,我認為 cron 具備 「Keep It Simple, Stupid(KISS,單純就是美)」 原則的優點。它使後台處理保持簡單。不需要編寫一直運行的多執行緒的作業處理應用程式(因此不會有記憶體洩漏),而是由 cron 啟動一個簡單的批次腳本。這個腳本判斷是否有作業要處理,執行作業,然後退出。不需要擔心內存洩漏。也不需要擔心線程停止或陷入無限循環。
那麼,cron 是如何運作的?這依賴於您所處的系統環境。我只討論老式簡單的 cron 的 UNIX 命令列版本,您可以向系統管理員諮詢如何在自己的 Web 應用程式中實現它。
下面是一個簡單的 cron 配置,它在每天晚上 11 點運行一個 PHP 腳本:
0 23 * * * jack /usr/bin/php /users/home/jack/myscript.php
前 5 個字段定義應該啟動腳本的時間。然後是應該用來運行這個腳本的使用者名稱。其餘的命令是要執行的命令列。時間欄位分別是分、小時、月中的日、月和週中的日。下面是幾個範例。
指令: 
15 * * * * jack /usr/bin/php /users/home/jack/myscript.php
在每個小時的第 15 分鐘執行腳本。 
指令:
15,45 * * * * jack /usr/bin/php /users/home/jack/myscript.php
在每個小時的第 15 和第 45 分鐘執行腳本。
指令:
*/1 3-23 * * * jack /usr/bin/php /users/home/jack/myscript.php
在早上 3 點到晚上 11 點之間的每分鐘運行腳本。
指令
30 23 * * 6 jack /usr/bin/php /users/home/jack/myscript.php
在每星期六的晚上 11:30 執行腳本(星期六由 6  指定)。
可以看到,組合的數量是無限的。可以根據需要控制運行腳本的時間。也可以指定多個要運行的腳本,這樣的話,有些腳本可以每分鐘都運行,而其他腳本(例如備份腳本)可以每天只運行一次。
為了指定將報告的錯誤發送到哪個電子郵件地址,可以使用 MAILTO 指令,如下所示:
MAILTO=jherr@pobox.com
注意:對於 Microsoft® Windows® 用戶,有一個等效的 Scheduled Tasks 系統可用來定期啟動命令列進程(例如 PHP 腳本)。
 回頁首 
批次體系結構的基礎知識
批次處理是相當簡單的。在大多數情況下,採用兩個工作流程之一。第一個工作流程用於進行報告;腳本每天運行一次,它產生報告並將報告發送給一組使用者。第二個工作流程是在回應某種請求時所建立的批次作業。例如,我登入 Web 應用程式中,並要求它向系統中註冊的所有使用者發送一個訊息,將一個新的特性告訴他們。這個操作必須進行批次處理,因為系統中有 10,000 個使用者。 PHP 要花一段時間才能完成這樣的任務,所以它必須由瀏覽器以外的作業來執行。
在第二個工作流程中,Web 應用程式只需將資訊放在某個位置,讓批次應用程式共享它。這些資訊指定作業的性質(例如,「Send this e-mail to all the people on the system」。)批次程式執行這個作業,然後刪除作業。另一種方法是,處理程序將作業標為已完成。無論用哪種方法,作業都應該識別為已完成,這樣就不會再次執行它。
本文的其餘部分示範在 Web 應用程式前端和批次後端之間共享資料的各種方法。
 回頁首 
郵件佇列
第一種方法是使用專用的郵件佇列系統。在這種模型中,資料庫中的一個表包含應該發送給各個使用者的電子郵件訊息。 Web 介面使用 mailouts 類別將電子郵件新增至佇列。電子郵件處理程序使用 mailouts 類別檢索未處理的電子郵件,然後再次使用它從佇列中刪除未處理的電子郵件。
這個模型首先需要 MySQL 模式。
清單 1. mailout.sql
    DROP TABLE IF EXISTS mailouts;CREATE TABLE mailouts (  id MEDIUM_RNOT to_address TEXT NOT NULL,  subject TEXT NOT NULL,  content TEXT NOT NULL,  PRIMARY KEY ( id ));
這個模式很簡單。每行中有一個 from 和一個 to 地址,以及電子郵件的主題和內容。
資料庫中的 mailouts 表處理的是 PHP mailouts 類別。
清單 2. mailouts.php
    getMessage());  . $id )  {    $db = Mailouts::get_db();    $sth = $db->prepare( 'DELETE FROM mail)   return true; }  public static function add( $from, $to, $subject, $content )  {    $db = Mailouts::get_db ull,?,?, ?,?)' );    $db->e​​xecute( $sth, array( $from, $to, $subject, $content ) );     Mailouts::get_db ();    $res = $db->query( "SELECT * FROM mailouts" );    $rows = array }    return $rows;  }}?>
這個腳本包含 Pear::DB 資料庫存取類別。然後定義 mailouts 類,其中包含三個主要的靜態函數:add、delete 和 get_all。 add() 方法在佇列中新增一個電子郵件,這個方法由前端使用。 get_all() 方法從表中傳回所有資料。 delete() 方法刪除一個電子郵件。
您可能會問,我為什麼不只在腳本末尾調用 delete_all() 方法。不這麼做有兩個原因:如果在發送每個訊息之後刪除它,那麼即使腳本在出現問題之後重新運行,訊息也不可能發送兩次;在批作業的啟動和完成之間可能會添加新的消息。
下一步是編寫一個簡單的測試腳本,這個腳本將一個條目加入佇列。
清單 3. mailout_test_add.php
    
在這個範例中,我新增一個 mailout,這個訊息要傳送給某公司的 Molly,其中包含主題 「TestSubject」 和電子郵件主體。可以在命令列上執行這個腳本:php mailout_test_add.php。
為了發送電子郵件,需要另一個腳本,這個腳本作為作業處理程序。
清單 4. mailout_send.php
    
這個腳本使用 get_all() 方法擷取所有電子郵件訊息,然後使用 PHP 的 mail() 方法逐一傳送訊息。在每次成功傳送電子郵件之後,呼叫 delete() 方法從佇列中刪除對應的記錄。
使用 cron 守護程式定期執行這個腳本。運行這個腳本的頻率取決於您的應用程式的需要。
注意:PHP Extension and Application Repository(PEAR)儲存庫包含一個出色的 郵件佇列系統 實現,並可免費下載。
 回頁首 
更通用的方法
專門用來發送電子郵件的解決方案是很不錯,但是是否有更通用的方法?我們需要能夠傳送電子郵件、產生報表或執行其他耗費時間的處理,而不必在瀏覽器中等待處理完成。
為此,可以利用一個事實:PHP 是一種解釋型語言。可以將 PHP 程式碼儲存在資料庫中的佇列中,以後再執行它。這需要兩個表,請參閱清單 5。
清單 5. generic.sql
    DROP TABLE IF EXISTS processing_items;CREATE TABLE processing_items (  id ,  PRIMARY KEY ( id ));DROP TABLE IF EXISTS processing_args;CREATE TABLE processing_args ( id MEDIUMINT NOT NULL AUTO_INCREMENT,  item_id MEDIUMINT NOT NULL,  key_name TEXT NOT NULL,  害sing_items 包含作業處理程序呼叫的函數。第二個表 processing_args 包含要傳送給函數的參數,所採用的形式是由鍵/值對所組成的 hash 表。
與 mailouts 表一樣,這兩個表也是由 PHP 類別包裝,而這個類別稱為 ProcessingItems。
清單 6.generic.php
    prepare('從processing_args中刪除item_id=?');    $db->xecute($sth, $id);    $sth=$db->prepare('從processing_items WHERE id=?'刪除);    $db->xecute($sth, $id);    回傳 true;  }  公共靜態函數 add($function, $args)  {    $db = ProcessingItems::get_db();    $sth = $db->prepare('插入到processing_items VALUES (null,?)');    $db->xecute($sth, array($function));    $res = $db->query("SELECT Last_insert_id()");    $id = 空;    while($res->fetchInto($row))){$id=$row[0]; }    foreach($args as $key => $value)    {        $sth = $db->prepare('INSERT INTO proces         $db->xecute($sth, array($id, $key, $value));    }    回傳 true;  }  公共靜態函數 get_all() {    $db = ProcessingItems::get_db();    $res = $db->query(“SELECT * FROM processing_items”);    $行=陣列();    while($res->fetchInto($row)))    {        $item = array();        $item['id'] = $row[0];        $item['function'] = $row[1];        $item['args'] = array();        $ares = $db->query(「從   processing_args WHERE item_id=?」選擇 key_name, value,$item['id']);        while($ares->fetchInto($arow))            $item['args'][ $arow[0] ] = $arow[1];        $行[]=$項目;    }    回傳$行;  }}?與mailouts系統一樣,介面使用add(),處理引擎使用get_all()
清單7所示的測試腳本將一個項目加入處理佇列。 ('value ' => 'foo') );?>
在這個範例中,新增了一個對printvalue 函數的,將value 參數設定為foo。了呼叫檸檬酸中的方法。 value']."n ";}foreach(ProcessingItems::get_all() as $item) { call_user_func_array($item['function'],    array($item['args'] ) );  ProcessingItems::delete($item['id']);}?>
這個腳本非常簡單。呼叫這個方法。
為了示範這個功能,我們看看在命令列上發生了什麼:
% php generic_test_add.php % php generic_process.php Printing: foo%
輸出並不多,但是您能夠看出要點。透過此機制,可以將任何 PHP 函數的處理延遲。
現在,如果您不喜歡將 PHP 函數名稱和參數放進資料庫中,那麼另一種方法是在 PHP 程式碼中建立資料庫中的 「處理作業類型」 名稱和實際 PHP 處理函數之間的對應。依照這個方式,如果以後決定修改 PHP 後端,只要 「處理作業類型」 字串匹配,系統就仍然可以運作。
 回頁首 
放棄資料庫
最後,我示範另一個稍微不同的解決方案,它使用一個目錄中的檔案來儲存批次作業,而不是使用資料庫。在這裡提供這個思路並不是建議您 “採用這種方式,而不使用資料庫”,這只是一種可供選擇的方式,是否採用它由您決定。
顯然,這個解決方案中沒有模式,因為我們不使用資料庫。所以先寫一個類,它包含與前面範例相似的 add()、get_all() 和 delete() 方法。
清單 9.batch_by_file.php
     $v)    {        fprintf($fh, $k.":".$v."n");    }    fclose($fh);    回傳 true;  }  公共靜態函數 get_all() {    $rows = array();    if (is_dir(BATCH_DIRECTORY)) {        if ($dh = opendir(BATCH_DIRECTORY)) {     {                $path = BATCH_DIRECTORY.$file;                if ( is_dir( $path ) == false )                {                          $item['id'] = $path;                    $fh=fopen($path,'r');                    if ( $fh )                    {       s( $fh ));                        $item['args'] = array();                        while( ( $line = fgets( $fh ) ) !=null )                             $args = split( ':', trim($line) );                            $item['args'][$args[0]] = $args[1];                        }                        以$rows []=$item;                        fclose($fh);                    }                }            }            closedir($dh);        }    }    return 這個類別不存取資料庫,而是讀寫 batch_items 目錄中的檔案。
使用下列測試程式碼新增新的批次條目。
清單 10. batch_by_file_test_add.php
    最後是處理程序的程式碼。
清單 11. batch_by_file_processor.php
    這段程式碼幾乎與資料庫版本完全相同,只是修改了檔案名稱和類別名稱。
 回頁首 
結束語
如前面提到的,伺服器對執行緒提供了許多支持,可以進行後台批次處理。在某些情況下,使用輔助線程處理小作業肯定比較容易。但是,也可以使用傳統工具(cron、MySQL、標準的物件導向的 PHP 和 Pear::DB)在 PHP 應用程式中建立批次作業,這很容易實現、部署和維護。
參考資料 
學習
您可以參考本文在 developerWorks 全球站點的 英文原文 。
閱讀 IBM developerWorks 的 PHP 專案資源中心,進一步了解 PHP。
PHP.net 是針對 PHP 開發人員的優良資源。
PEAR Mail_Queue 包 是一個健全的郵件佇列實現,其中包括資料庫後端。
crontab 手冊 提供了 cron 配置的細節,但是不容易理解。
PHP 手冊中關於 Using PHP from the command line 的一節可以幫助您了解如何從 cron 運行腳本。
隨時關注 developerWorks 技術事件和 webcast。
了解世界各地即將進行的會議、展覽、網路廣播和其他 活動,IBM 開放原始碼開發人員可以透過這些活動了解最新的技術發展。
存取 developerWorks 開源技術專區,獲得廣泛的 how-to 資訊、工具和專案更新,可協助您利用開放原始碼技術開發並與 IBM 產品結合使用。
developerWorks podcasts 中包含許多適合於軟體開發人員的有趣的訪談和討論。
取得產品與技術
參考 PEAR -- PHP Extension and Application Repository,其中包含 Pear::DB。
使用 IBM 試用軟體 改善您的下一個開放原始碼開發項目,這些軟體可以下載或透過 DVD 取得。
討論
developerWorks PHP Developer Forum 為所有 PHP 開發人員提供了討論技術問題的場所。如果您有關於 PHP 腳本、函數、語法、變數、除錯和其他主題的問題,可以在這裡提出。
透過參與 developerWorks blog 加入 developerWorks 社群。 
關於作者
  Jack D. Herrington 是一位資深軟體工程師,並具有 20 多年的工作經驗。他撰寫過三本書: Code Generation in Action 、 Podcasting Hacks 和 PHP Hacks,並撰寫了 30 多篇文章。

以上就介紹了批次之家 PHP 中的批次的實現,包括了批次之家方面的內容,希望對PHP教程有興趣的朋友有所幫助。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板