簡(jiǎn)介
PHP 的異步進(jìn)程助手,借助于 AMQP 實(shí)現(xiàn)異步執(zhí)行 PHP 的方法,將一些很耗時(shí)、追求高可用、需要重試機(jī)制的操作放到異步進(jìn)程中去執(zhí)行,將你的 HTTP 服務(wù)從繁重的業(yè)務(wù)邏輯中解脫出來。以一個(gè)較低的成本將傳統(tǒng) PHP 業(yè)務(wù)邏輯轉(zhuǎn)換成非阻塞、高可用、可擴(kuò)展的異步模式。
依賴
- php 5.6+
- ext-bcmath
- ext-amqp 1.9.1+
- ext-memcached 3.0.3+
安裝
通過 composer 安裝
composer require l669/async-helper
或直接下載項(xiàng)目源碼
wget https://github.com/l669306630/async-helper/archive/master.zip
使用范例
業(yè)務(wù)邏輯:這里定義了很多等待被調(diào)用的類和方法,在你的項(xiàng)目中這可能是數(shù)據(jù)模型、或是一個(gè)發(fā)送郵件的類。
<?php class SendMailHelper { /** * @param array $mail * @throws Exception */ public static function request($mail) { // 在這里發(fā)送郵件,或是通過調(diào)用第三方提供的服務(wù)發(fā)送郵件 // 發(fā)送失敗的時(shí)候你拋出了異常,希望被進(jìn)程捕獲,并按設(shè)定的規(guī)則進(jìn)行重試 } }
生產(chǎn)者:通常是 HTTP 服務(wù),傳統(tǒng)的 PHP 項(xiàng)目或是一個(gè)命令行程序,接收到某個(gè)請(qǐng)求或指令后進(jìn)行一系列的操作。
<?php use l669\AsyncHelper; class UserController { public function register() { // 假設(shè)這是一個(gè)用戶注冊(cè)的請(qǐng)求,用戶提交了姓名、郵箱、驗(yàn)證碼 // 第一步、校驗(yàn)用戶信息 // 第二步、實(shí)例化異步助手,這時(shí)候會(huì)連接 AMQP $async_helper = new AsyncHelper([ 'host' => '127.0.0.1', 'port' => '5672', 'user' => 'root', 'pass' => '123456', 'vhost' => '/' ]); // 第三步、保存用戶信息到數(shù)據(jù)庫 $mail = [ 'from' => 'service@yourdomain.com', 'to' => 'username@163.com', 'subject' => '恭喜你注冊(cè)成功', 'body' => '請(qǐng)點(diǎn)擊郵件中的鏈接完成驗(yàn)證....' ]; // 第四步、通過異步助手發(fā)送郵件 $async_helper->run('\\SendMailHelper', 'request', [$mail]); // 這是同步的模式去發(fā)送郵件,如果郵件服務(wù)響應(yīng)遲緩或異常,就會(huì)直接影響該請(qǐng)求的響應(yīng)時(shí)間,甚至丟失這封重要郵件 // SendMailHelper::request($mail); } }
消費(fèi)者:PHP 的異步進(jìn)程,監(jiān)聽消息隊(duì)列,執(zhí)行你指定的方法。并且該消費(fèi)者進(jìn)程是可擴(kuò)展的高可用的服務(wù),這一切都得益于 AMQP,這是系統(tǒng)解耦、布局微服務(wù)的最佳方案。
consume.php
<?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); } }
# 在命令行下啟動(dòng)消費(fèi)者進(jìn)程,推薦使用 supervisor 來管理進(jìn)程
php consume.php
支持事務(wù):需要一次提交執(zhí)行多個(gè)異步方法,事務(wù)可以確保完成性。
// 接著上面的示例來說,這里省略了一些重復(fù)的代碼,下同 $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(); }
阻塞式重試:當(dāng)異步進(jìn)程執(zhí)行一個(gè)方法,方法內(nèi)部拋出異常時(shí)進(jìn)行重試,一些必須遵循執(zhí)行順序的業(yè)務(wù)就要采用阻塞式的重試,通過指定重試最大阻塞時(shí)長(zhǎng)來控制。
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 // 最長(zhǎng)重試 10 分鐘 ]); $send_mail_helper = new \SendMailHelper(); $mail = new \stdClass(); $mail->from = 'service@yourdomain.com'; $mail->to = 'username@163.com'; $mail->subject = '恭喜你注冊(cè)成功'; $mail->body = '請(qǐng)點(diǎn)擊郵件中的鏈接完成驗(yàn)證....'; $async_helper->run($send_mail_helper, 'request', [$mail]); // 如果方法中需要拋出異常來結(jié)束程序,又不希望被異步進(jìn)程重試,可以拋出以下幾種錯(cuò)誤碼,進(jìn)程捕獲到這些異常后會(huì)放棄重試: // l669\AsyncException::PARAMS_ERROR // l669\AsyncException::METHOD_DOES_NOT_EXIST // l669\AsyncException::KNOWN_ERROR