1. 程式人生 > 程式設計 >Laravel框架中佇列和工作(Queues、Jobs)操作例項詳解

Laravel框架中佇列和工作(Queues、Jobs)操作例項詳解

在我們的web應用中,經常會遇到這樣的情況:

使用者在進行了某項操作後,我們需要在後臺完成一個耗時且耗費資源的任務,以對應使用者的操作。

通常來說,web應用中的操作都是同步的(synchronous),即使用者的操作可以立即得到回饋。

但是在以上情況下,同步等待操作結果將是災難性的。比如使用者點選了申請密碼重置郵件,倘若我們讓使用者一直停滯在等待頁面,直至郵件傳送成功,那麼使用者體驗將非常地不好,因為有時候可能需要很長的時間才能將郵件傳送完成。

從另一個角度來說,如果我們伺服器處於高負荷的情況,當多個使用者同時請求傳送郵件等操作時,我們不希望同時地給伺服器增加負荷,否則可能會導致伺服器崩潰,造成無法預估的情況。

從以上的討論可以看出,我們需要一種機制,可以非同步地響應使用者操作,並且不會給伺服器增加過大的負荷。

那麼這樣一種機制就是Queues和Jobs(即佇列和工作)。

如果你係統地學習過電腦科學,那麼佇列的概念你應該不陌生。假設我們去銀行辦事,我們拿了一個號,發現前面有8個人在等待,那麼我們實際上就處在一個佇列之中,佇列中靠前的人會先被叫到號碼,並且叫號的順序即拿號的順序。這樣的佇列就叫做Queue,採用的是先到先處理的方式,不允許插隊的情況存在。而我們要辦的事情就叫Job。

在Laravel中,我們可以很方便地使用Queues及Jobs來達到我們的目的。首先我們需要先來看一下,Laravel中有哪些Queues。

開啟config/queue.php,我們可以看到幾種常見的佇列設定:

return [      
  
  /*      
  |--------------------------------------------------------------------------      
  | Default Queue Connection Name      
  |--------------------------------------------------------------------------      
  |      
  | Laravel's queue API supports an assortment of back-ends via a single      
  | API,giving you convenient access to each back-end using the same      
  | syntax for every one. Here you may define a default connection.      
  |      
  */      
  
  'default' => env('QUEUE_DRIVER','sync'),/*      
  |--------------------------------------------------------------------------      
  | Queue Connections      
  |--------------------------------------------------------------------------      
  |      
  | Here you may configure the connection information for each server that      
  | is used by your application. A default configuration has been added      
  | for each back-end shipped with Laravel. You are free to add more.      
  |      
  | Drivers: "sync","database","beanstalkd","sqs","redis","null"      
  |      
  */      
  
  'connections' => [      
  
    'sync' => [      
      'driver' => 'sync',],'database' => [      
      'driver' => 'database','table' => 'jobs','queue' => 'default','retry_after' => 90,'beanstalkd' => [      
      'driver' => 'beanstalkd','host' => 'localhost','sqs' => [      
      'driver' => 'sqs','key' => env('SQS_KEY','your-public-key'),'secret' => env('SQS_SECRET','your-secret-key'),'prefix' => env('SQS_PREFIX','https://sqs.us-east-1.amazonaws.com/your-account-id'),'queue' => env('SQS_QUEUE','your-queue-name'),'region' => env('SQS_REGION','us-east-1'),'redis' => [      
      'driver' => 'redis','connection' => 'default','block_for' => null,/*      
  |--------------------------------------------------------------------------      
  | Failed Queue Jobs      
  |--------------------------------------------------------------------------      
  |      
  | These options configure the behavior of failed queue job logging so you      
  | can control which database and table are used to store the jobs that      
  | have failed. You may change them to any database / table you wish.      
  |      
  */      
  
  'failed' => [      
    'database' => env('DB_CONNECTION','mysql'),'table' => 'failed_jobs',];

在connections中,我們看到sync這個連線。sync是Laravel預設的佇列,代表的就是synchronous,即同步佇列。

今天我們要來看一下,如何使用database,即資料庫來實現非同步任務處理。

要使用database來作為佇列的內部實現機制,我們需要建立一張用於儲存Jobs的表:

$ php artisan queue:table     
$ php artisan migrate

以上命令將會在資料庫建立名為jobs的表。

佇列我們有了,那麼現在我們來看一下Jobs。Laravel中jobs檔案預設位置在app/Jobs資料夾下,我們可以通過make:job這個Artisan命令快速建立我們的job類:

$ php artisan make:job SendEmail

生成的job會實現Illuminate\Contracts\Queue\ShouldQueue這個介面,表明生成的job物件將被推到佇列中進行非同步處理。

job類其實很簡單,裡面只有一個名為handle的方法,該方法在job被queue處理的時候自動被呼叫。

在上面的命令中,我們建立了一個名為SendEmail的類:

<?php    
  
namespace App\Jobs;    
  
use App\Email;    
use Illuminate\Bus\Queueable;    
use Illuminate\Queue\SerializesModels;    
use Illuminate\Queue\InteractsWithQueue;    
use Illuminate\Contracts\Queue\ShouldQueue;    
use Illuminate\Foundation\Bus\Dispatchable;    
  
class SendEmail implements ShouldQueue    
{    
  use Dispatchable,InteractsWithQueue,Queueable,SerializesModels;    
  
  protected $email;    
  
  /**    
   * Create a new job instance.    
   *    
   * @param Podcast $podcast    
   * @return void    
   */    
  public function __construct(Email $email)    
  {    
    $this->email = $email;    
  }    
  
  /**    
   * Execute the job.    
   *    
   * @param AudioProcessor $processor    
   * @return void    
   */    
  public function handle()    
  {    
    // Process email and send the email to recipient(s)    
    // 這裡我們可以處理我們的郵件並將郵件傳送至接收人 
  }    
}

可以看到,我們可以將model傳遞進job的constructor中。Laravel會自動序列化(Serialize)模型的識別資訊,在job真正被處理的時候,完整的模型資料才會被從資料庫調用出來。另外,在handle方法中,我們也可以注入我們的依賴dependencies。

好了,現在我們有了job類,可以建立job物件了,那麼如何把job新增進佇列呢?

在我們的控制器中,我們可以呼叫job的dispatch方法來將其新增進佇列中:

<?php  
  
namespace App\Http\Controllers;  
  
use App\Jobs\SendEmail;  
use Illuminate\Http\Request;  
use App\Http\Controllers\Controller;  
use App\Email; 
  
class EmailsController extends Controller  
{  
  /**  
   * Store a new email.  
   *  
   * @param Request $request  
   * @return Response  
   */  
  public function send(Request $request)  
  {  
    // Create email...  
    // 這裡我們提取email資訊並建立$email,Email是我們自定義的Model 
    $email = Email::create($request->only('sender','to','content')); 
  
    SendEmail::dispatch($email);  
  }  
}

這樣一來,每當我們的控制器呼叫send方法時,就會建立一個SendEmail的job在資料庫中。

那麼怎麼樣呼叫Queue Worker來處理我們的jobs呢?

在.env檔案中,我們將QUEUE_DRIVER=sync改為QUEUE_DRIVER=database。

接下來,我們執行以下Artisan命令:

$ php artisan queue:work

佇列的worker會一直執行,每當有任務被新增進資料庫jobs表中,worker便會自動抓取出任務進行處理。當任務失敗時,worker會重複執行任務,直至最大嘗試次數(預設為255)。我們可以手動設定最大嘗試次數:

$ php artisan queue:work --tries=3

當然,我們也可以手動設定任務的超時(預設90s,在config/queue.php中的retry_after設定):

$ php artisan queue:work --timeout=30

最後,當沒有任務的時候,我們可以設定一個睡眠時間,當worker在睡眠時間時,將不會處理任務:

$ php artisan queue:work --sleep=10

上面的命令意思是每當worker處理完所有任務後,會睡眠10s,然後才會再次檢查任務佇列

本文使用Laravel 5.6進行講解

本文主要講解了Laravel框架中佇列和工作(Queues、Jobs)操作例項詳解,更多關於Laravel框架的使用技巧請檢視下面的相關連結