Tuts: Laravel queues , cách hoạt động và thực hiện bằng Amazon SQS (Phần 2)

Spread the love

Tiếp tục phần trước với cô thơ kí của chúng ta, phần tiếp theo sẽ bàn về

  • Nội dung Message của Queue
  • Các events liên quan đến việc xử lý Jobs

Nội dung Message của Queue


Đã bao giờ bạn tự hỏi, tại sao cô thơ kí lại có thể xử lý công việc trong hàng đợi một cách trơn tru đến thế ?
Và rằng một hàng đợi có thể chứa được nhiều hơn một loại message không nhỉ ? Ví dụ cô thơ kí của chúng ta có thể xử lý cả việc công chứng giấy tờ, cũng như kiêm luôn việc bán bảo hiểm y tế khi có người cần. Vậy làm thế nào để cô ta có thể phân biệt đâu là người muốn mua bảo hiểm y tế và đâu là người muốn công chứng giấy tờ ? Ta sẽ xem xét nội dung message được gửi lên hàng đợi như bên dưới

 public function postRegister(
 RegisterRequest $request,
 UserRepository $user_gestion)
 {
     $user = $user_gestion->store(
     $request->all(),
     $confirmation_code = str_random(30)
     );
     $job = (new SendRegisterEmail($user))->delay(30);
     $this->dispatch($job);
     return redirect('/')->with('ok', trans('front/verify.message'));
 }

Rất đơn giản nội dung của đoạn code này chỉ là để dispatch Jobs gửi mail lên Queue khi mà user đã đăng kí xong, với tham số là biến $user mà chúng ta vừa tạo, mình thêm phần delay(30) để dễ confirm trên AWS, vì nếu không set delay thì message vừa gửi sẽ được xử lý mất không kịp chụp hình cho các bạn 🙂

Màn hình lúc đó sẽ thế này

regist_ok

Lúc này thì trên Queue nội dung message sẽ như thế nào nhỉ ? Ở đây mình sẽ sử dụng AWS SQS để check nội dung message. Mình tạm thời điền thông tin vào form như bên dưới:

user_regist_input

Bấm nút Send thì màn hình đăng kí OK sẽ được hiển thị ra. Và đây là nội dung message trên Queue của AWS

Đây là nội dung Message trên Queue của AWS

sqs_message

và chi tiết thì như hình bên dưới

sqs_details

Nội dung chi tiết của message

{"job":"Illuminate\\Queue\\CallQueuedHandler@call","data":{"commandName":"App\\Jobs\\SendRegisterEmail","command":"O:26:\"App\\Jobs\\SendRegisterEmail\":5:{s:7:\"\u0000*\u0000user\";O:45:\"Illuminate\\Contracts\\Database\\ModelIdentifier\":2:{s:5:\"class\";s:15:\"App\\Models\\User\";s:2:\"id\";i:31;}s:10:\"connection\";N;s:5:\"queue\";N;s:5:\"delay\";i:30;s:6:\"\u0000*\u0000job\";N;}"}}

Ta thấy các điểm đáng quan trọng sau

  • Message là một JSON data trong đó job gọi đến hàm call của class CallQueuedHandler và data chứa thông tin để xử lý.
  • Trong property data bao gồm các đối tượng sau
    • commandName, command là định danh của Jobs sẽ được thực hiện (vd ở đây nếu bạn không muốn gửi mail thì command sẽ là một định danh của Jobs khác )
    • Illuminate\Contracts\Database\ModelIdentifier là một contracts sẽ được traits SerializesModels sử dụng để serialize object $user của chúng ta (serialize: có nghĩa là chuyển từ kiểu object của chúng ta thành kiểu serialize (dạng như kiểu chuỗi ấy) mà ở đó serialized object có thể dùng để gửi đi chỗ khác trên protocol http một cách dễ dàng). Ở đây các bạn có thể thấy, không phải toàn bộ thông tin của App\Models\User được gửi đi, mà chỉ cần gửi id của user vừa được tạo lên Queue là đủ. Hãy để ý đoạn  App\\Models\\User\";s:2:\"id\";i:31; chứng tỏ users mà chúng ta sẽ send email lusà $user có id là 31

Nào hãy xem thử user có id = 31 có đúng là user chúng ta vừa tạo khi nãy không nhé (user name là : littlething nhé )

user_db

Đúng là user_id = 31 chính là user mà chúng ta vừa mới đăng kí. Có nghĩa là Laravel đã tự động giảm thiểu nội dung của message gửi lên queue cho chúng ta bằng cách chỉ gửi lên phần ID của models thay vì gửi lên toàn bộ thông tin của model. Thật là cô thơ kí tiết kiệm và hiệu quả 🙂

Các event liên quan đến Jobs


Do việc thực hiện jobs là bất đồng bộ, nên việc xử lý các sự kiện liên quan đến Jobs cũng rất quan trọng. Bên dưới là list các events mà một liên quan đến vòng đời của 1 Jobs

  • JobProcessing : trigger ngay khi Job vừa được pop khỏi queue.
  • JobProcessed :  trigger ngay sau khi Job vừa được thực hiện xong
  • JobFailed        : trigger khi Job failed
  • JobExceptionOccurred : trigger khi xảy ra exception

Ví dụ bên dưới sẽ listen 2 sự kiện JobProcessing và JobProcessed.

Để thực hiện listen các events của Jobs ta sẽ implements vao trong class App\Providers\AppServiceProvider hàm boot()

 public function boot()
 {
    Validator::resolver(function($translator, $data, $rules, $messages)
   {
         return new Validation($translator, $data, $rules, $messages);
    });

    // Logging after send Mail
    Queue::after(function(JobProcessed $event){
       Log:info("Queue completed" . ($event->connectionName) . '/n' . json_encode($event->data));
    });

    Queue::before(function (JobProcessing $event) {
      Log:info("Connection name:" . $event->connectionName);
     });
 }

 

Như các bạn thấy, mình đã add thêm 2 hàm là  Queue::after(function(JobProcessed $event), Queue::before(function(JobProcessing $event) để bắt các sự kiện affter và before của Jobs

Ta có thể tham khảo class Worker hàm process để nắm rõ hơn các event sẽ được raise lên trong khi xử lý Queue như bên dưới

 /**
 * Process a given job from the queue.
 *
 * @param string $connection
 * @param \Illuminate\Contracts\Queue\Job $job
 * @param int $maxTries
 * @param int $delay
 * @return array|null
 *
 * @throws \Throwable
 */
 public function process($connection, Job $job, $maxTries = 0, $delay = 0)
 {
    if ($maxTries > 0 && $job->attempts() > $maxTries) {
      return $this->logFailedJob($connection, $job);
    }

    try {
       $this->raiseBeforeJobEvent($connection, $job);

       // First we will fire off the job. Once it is done we will see if it will be
       // automatically deleted after processing and if so we'll fire the delete
       // method on the job. Otherwise, we will just keep on running our jobs.
       $job->fire();

       $this->raiseAfterJobEvent($connection, $job);

      return ['job' => $job, 'failed' => false];
    } catch (Exception $e) {
      $this->handleJobException($connection, $job, $delay, $e);
    } catch (Throwable $e) {
      $this->handleJobException($connection, $job, $delay, $e);
    }
}

và kết quả chạy khi của đoạn code trên là từng message sẽ được log vào trong file (ở đây mình dùng file log chuẩn của laravel. Các bạn có thể config lại nơi chứa log từ queue để mang tính tập trung.

[2016-12-04 16:06:12] local.INFO: Connection name:sqs  
[2016-12-04 16:06:16] local.INFO: Queue completedsqs/n{"job":"Illuminate\\Queue\\CallQueuedHandler@call","data":{"commandName":"App\\Jobs\\SendRegisterEmail","command":"O:26:\"App\\Jobs\\SendRegisterEmail\":5:{s:7:\"\u0000*\u0000user\";O:45:\"Illuminate\\Contracts\\Database\\ModelIdentifier\":2:{s:5:\"class\";s:15:\"App\\Models\\User\";s:2:\"id\";i:30;}s:10:\"connection\";N;s:5:\"queue\";N;s:5:\"delay\";i:30;s:6:\"\u0000*\u0000job\";N;}"}}  

Các bạn có thể add thêm xử lý cho các sự kiện failed, cũng như là exception khi thực hiện Jobs.

Phần tiếp theo sẽ là tutorials hoàn chỉnh về việc thực hiện Queue trên AWS và So sánh tốc độ xử lý khi không dùng queue