1. 程式人生 > >「微信小程式」PHP非同步程序async-helper例項詳解

「微信小程式」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例項詳解的詳細內容!