「微信小程式」PHP非同步程序async-helper例項詳解
PHP非同步程序async-helper例項詳解
PHP 的非同步程序助手,藉助於 AMQP 實現非同步執行 PHP 的方法,將一些很耗時、追求高可用、需要重試機制的操作放到非同步程序中去執行,將你的 HTTP 服務從繁重的業務邏輯中解脫出來。以一個較低的成本將傳統 PHP 業務邏輯轉換成非阻塞、高可用、可擴充套件的非同步模式。本文主要和大家介紹PHP非同步程序助手async-helper的詳細用法以及相關程式碼例項,對此有需要的朋友學習下。希望能幫助到大家。
依賴
php 5.6+
ext-bcmath
ext-amqp 1.9.1+
ext-memcached 3.0.3+
安裝
通過 composer 安裝
1 |
composer require l669/async-helper |
或直接下載專案原始碼
1 |
wget https://github.com/l669306630/async-helper/archive/master.zip |
使用範例
業務邏輯:這裡定義了很多等待被呼叫的類和方法,在你的專案中這可能是資料模型、或是一個傳送郵件的類。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php class SendMailHelper { /** * @param array $mail * @throws Exception */ public static function request($mail) { // 在這裡傳送郵件,或是通過呼叫第三方提供的服務傳送郵件 // 傳送失敗的時候你丟擲了異常,希望被程序捕獲,並按設定的規則進行重試 } } |
生產者:通常是 HTTP 服務,傳統的 PHP 專案或是一個命令列程式,接收到某個請求或指令後進行一系列的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php use l669\AsyncHelper; class UserController { public function register() { // 假設這是一個使用者註冊的請求,使用者提交了姓名、郵箱、驗證碼 // 第一步、校驗使用者資訊 // 第二步、例項化非同步助手,這時候會連線 AMQP $async_helper = new AsyncHelper([ 'host' => '127.0.0.1', 'port' => '5672', 'user' => 'root', 'pass' => '123456', 'vhost' => '/' ]); // 第三步、儲存使用者資訊到資料庫 $mail = [ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => '恭喜你註冊成功', 'body' => '請點選郵件中的連結完成驗證....' ]; // 第四步、通過非同步助手傳送郵件 $async_helper->run('\\SendMailHelper', 'request', [$mail]);
// 這是同步的模式去傳送郵件,如果郵件服務響應遲緩或異常,就會直接影響該請求的響應時間,甚至丟失這封重要郵件 // SendMailHelper::request($mail); } } |
消費者:PHP 的非同步程序,監聽訊息佇列,執行你指定的方法。並且該消費者程序是可擴充套件的高可用的服務,這一切都得益於 AMQP,這是系統解耦、佈局微服務的最佳方案。
consume.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php require_once('vendor/autoload.php'); require_once('SendMailHelper.php');
use l669\AsyncHelper; use l669\CacheHelper;
$cache_helper = new CacheHelper('127.0.0.1', 11211); while(true){ try{ $async_helper = new AsyncHelper([ 'host' => '127.0.0.1', 'port' => '5672', 'user' => 'root', 'pass' => '123456', 'vhost' => '/', 'cacheHelper' => $cache_helper ]); $async_helper->consume(); }catch(Exception $e){ // 可以在這裡記錄一些日誌 sleep(2); } } |
# 在命令列下啟動消費者程序,推薦使用 supervisor 來管理程序
php consume.php
支援事務:需要一次提交執行多個非同步方法,事務可以確保完成性。
1 2 3 4 5 6 7 8 9 10 |
// 接著上面的示例來說,這裡省略了一些重複的程式碼,下同 $async_helper->beginTransaction(); try{ $async_helper->run('\\SendMailHelper', 'request', [$mail1]); $async_helper->run('\\SendMailHelper', 'request', [$mail2]); $async_helper->run('\\SendMailHelper', 'request', [$mail3]); $async_helper->commit(); }catch(\Exception $e){ $async_helper->rollback(); } |
阻塞式重試:當非同步程序執行一個方法,方法內部丟擲異常時進行重試,一些必須遵循執行順序的業務就要採用阻塞式的重試,通過指定重試最大阻塞時長來控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
use l669\CacheHelper; use l669\AsyncHelper; $async_helper = new AsyncHelper([ 'host' => '127.0.0.1', 'port' => '5672', 'user' => 'root', 'pass' => '123456', 'vhost' => '/', 'cacheHelper' => new CacheHelper('127.0.0.1', 11211), 'retryMode' => AsyncHelper::RETRY_MODE_REJECT, // 阻塞式重試 'maxDuration' => 600 // 最長重試 10 分鐘 ]); $send_mail_helper = new \SendMailHelper(); $mail = new \stdClass(); $mail->from = '[email protected]'; $mail->to = '[email protected]'; $mail->subject = '恭喜你註冊成功'; $mail->body = '請點選郵件中的連結完成驗證....'; $async_helper->run($send_mail_helper, 'request', [$mail]);
// 如果方法中需要丟擲異常來結束程式,又不希望被非同步程序重試,可以丟擲以下幾種錯誤碼,程序捕獲到這些異常後會放棄重試: // l669\AsyncException::PARAMS_ERROR // l669\AsyncException::METHOD_DOES_NOT_EXIST // l669\AsyncException::KNOWN_ERROR |
非阻塞式重試:當非同步執行的方法內部丟擲異常,async-helper 會將該方法重新放進佇列的尾部,先執行新進入佇列的方法,回頭再重試剛才執行失敗的方法,通過指定最大重試次數來控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
use l669\CacheHelper; use l669\AsyncHelper; $async_helper = new AsyncHelper([ 'host' => '127.0.0.1', 'port' => '5672', 'user' => 'root', 'pass' => '123456', 'vhost' => 'new', 'cacheHelper' => new CacheHelper('127.0.0.1', 11211), 'queueName' => 'emails.vip', // 給付費的大爺走 VIP 佇列 'retryMode' => AsyncHelper::RETRY_MODE_TTL, // 非阻塞式重試 'maxRetries' => 10 // 最多重試 10 次 ]); $mail = new \stdClass(); $mail->from = '[email protected]'; $mail->to = '[email protected]'; $mail->subject = '恭喜你註冊成功'; $mail->body = '請點選郵件中的連結完成驗證....'; $async_helper->run('\\SendMailHelper', 'request', [$mail]); |
應用和解惑
我們採用的是開源的 RabbitMQ 來為我們提供的 AMQP 服務。
你的專案部署在擁有很多伺服器節點的叢集上,每個節點的程式都需要寫日誌檔案,現在的問題就是要收集所有節點上面的日誌到一個地方,方便我們及時發現問題或是做一些統計。所有節點都可以使用 async-helper 非同步呼叫一個寫日誌的方法,而執行這個寫日誌的方法的程序只需要在一臺機器上啟動就可以了,這樣所有節點的日誌就都實時掌握在手裡了。
做過微信公眾號開發的都知道,騰訊微信可以將使用者的訊息推送到我們的伺服器,如果我們在 5s 內未及時響應,騰訊微信會重試 3 次,其實這就是訊息佇列的應用,使用 async-helper 可以輕鬆的做和這一樣的事情。
得益於 RabbitMQ,你可以輕鬆的橫向擴充套件你的消費者程序的能力,因為 RabbitMQ 天生就支援叢集部署,你可以輕鬆的啟動多個消費者程序,或是將消費者程序分佈到多臺機器上。
如果 RabbitMQ 服務不可用怎麼辦呢?部署 RabbitMQ 高可用服務是容易的,對外提供單一 IP,這個 IP 是個負載均衡,背後是 RabbitMQ 叢集,負載均衡承擔對後端叢集節點的健康檢查。
async-helper 能否承受高併發請求?async-helper 生產者使用的是短連線,也就說在你的 HTTP 還沒有響應瀏覽器的時候 async-helper 就已經結束了工作,你連線 RabbitMQ 的時間是百分之百小於 HTTP 請求的時間的,換言之,只要 RabbitMQ 承受併發的能力超過你的 HTTP 服務的承受併發的能力,RabbitMQ 就永遠不會崩,通過橫向擴充套件 RabbitMQ 很容易做到的。
和傳統 PHP 相比
對任何 PHP 方法通過反射進行非同步執行;
高可用,執行方法進入訊息佇列,可持久化,即使伺服器宕機,執行任務也不丟失;
高可用,對異常可以進行不限次數和時間的重試,重試次數和時間可配置;
支援對多個非同步方法包含在事務中執行,支援回滾事務;
方法的引數型別支援除資源型別(resource)和回撥函式(callable)外的任意型別的引數;
得益於 AMQP,非同步方法可以承受高併發、高負載,支援叢集部署、橫向擴充套件;
低延時,實測延時時間 0.016 ~ 0.021s;
適用於:日常資料庫操作、日誌收集、金融交易、訊息推送、傳送郵件和簡訊、資料匯入匯出、計算大量資料生成報表;
以上就是PHP非同步程序async-helper例項詳解的詳細內容!