[Laravel] Queues를 이용한 비동기 프로그래밍

[Laravel] Queues를 이용한 비동기 프로그래밍 updated_at: 2024-03-25 11:43

[Laravel] Queues를 이용한 비동기 프로그래밍

라라벨은 queues를 활용하면 대량의 메일 발송이나 시간이 많이 걸리는 프로세스등을 백그라운드로 처리할 수 있다.
여기서는 database를 활용한 큐를 처리하는 예제를 설명드릴텐데 전반적인 그림은 아래와 같다.

  • 사용자가 프로그램실행 > database에 실행할 프로그램 입력 > cron을 이용하여 프로그램 실행

라라벨의 Queues 사용하기

1. jobs 테이블 생성하기

php artisan queue:table
> Migration created successfully!

jobs라는 테이블이 생성됩니다. 생성을 수동으로 하시려면 아래 query를 이용하시기 바랍니다.

CREATE TABLE `jobs` (
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
	`queue` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_ci',
	`payload` LONGTEXT NOT NULL COLLATE 'utf8mb4_unicode_ci',
	`attempts` TINYINT(3) UNSIGNED NOT NULL,
	`reserved_at` INT(10) UNSIGNED NULL DEFAULT NULL,
	`available_at` INT(10) UNSIGNED NOT NULL,
	`created_at` INT(10) UNSIGNED NOT NULL,
	PRIMARY KEY (`id`) USING BTREE,
	INDEX `jobs_queue_index` (`queue`) USING BTREE
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
;

2. QUEUE_CONNECTION (.env) 설정

config/queue.php 파일을 보면 default가 sync로 설정되어 있고 다양한 connection이 있음을 확인 할 수 있다
저는 database를 사용할 예정이므로 .env 파일에서 QUEUE_CONNECTION=database 로 변경하였습니다.
아래 database에서 table을 위에서 만든 jobs를 사용하고 있음을 알 수 있습니다.

'default' => env('QUEUE_CONNECTION', 'sync'),
'connections' => [
    'sync' => [
      ..........
    ],
    'database' => [
        'driver' => 'database',
        'table' => 'jobs',
        'queue' => 'default',
        'retry_after' => 90,
        'after_commit' => false,
    ],
    'beanstalkd' => [],
    'sqs' => [],
    'redis' => [],
  ],

3. Job 제작

php artisan make:job IndexNow

명령을 실행하면 app/Jobs 폴더안에 IndexNow.php파일이 생성된다.
이곳에서 우리는 필요한 프로그램을 하여야 하는데 먼저 기본적인 모양만 보여드리기 위해 생성된 파일 그대로를 나열하였습니다.

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class IndexNow implements ShouldQueue
{
  use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

  /**
   * Create a new job instance.
   *
   * @return void
   */
  public function __construct()
  {
    //
  }

  /**
   * Execute the job.
   *
   * @return void
   */
  public function handle()
  {
      //
  }
}

4. Dispatch the Job

job을 실행하기위해서 controller에서 코드를 작성하면 됩니다. 저는 NowIndex라는 코드를 샘플로 작성해 보겠습니다.

<?php
..........
use App\Jobs\IndexNow;
..........
class NowIndexController extends Controller
{
  ..........
  public function indexNow(Request $request) {
    IndexNow::dispatch($request->somedata);
    ..........
  }
}

5. jobs 테이블에 데이타가 들어갔는지 확인

위의 코드를 실행하면 위에서 만든 jobs 테이블에 데이타가 들어가게 됩니다.

id : 1
queue: default
payload: {"uuid":"f6b8dd54-5639-4a47-bcb9-b8cea590806c","displayName":"App\\Jobs\\IndexNow","job":"Illuminate\\Queue\\CallQueuedHandler@call","maxTries":null,"maxExceptions":null,"failOnTimeout":false,"backoff":null,"timeout":null,"retryUntil":null,"data":{"commandName":"App\\Jobs\\IndexNow","command":"O:17:\"App\\Jobs\\IndexNow\":10:{s:3:\"job\";N;s:10:\"connection\";N;s:5:\"queue\";N;s:15:\"chainConnection\";N;s:10:\"chainQueue\";N;s:19:\"chainCatchCallbacks\";N;s:5:\"delay\";N;s:11:\"afterCommit\";N;s:10:\"middleware\";a:0:{}s:7:\"chained\";a:0:{}}"}}
attempts: 0
............

6. Queue Worker 실행하기

jobs 테이블에 들어가 있는 job을 실행하기위해서는 queue worker를 실행하여야 합니다.

queue:work vs queue:listen

work 는 Job의 프로그램이 변경되면 다시 실행하여야 정삭으로 작동되지만 listen은 바로 적용된다.

  • work: 안정적인 운영(소스변경 검토 후 적용)을 필요로 하는 경우
  • listen: 변경된 소스를 실시간 적용
php artisan queue:listen // php artisan queue:work

> [2024-03-21 18:03:39][1] Processing: App\Jobs\IndexNow
> [2024-03-21 18:03:39][1] Processed:  App\Jobs\IndexNow

위의 방식은 터미널을 닫으면 실행되지 않으므로 아래와 같이 백그라운드로 실행하여야 한다.

nohup php artisan queue:work --daemon &
nohup php artisan queue:work --daemon >> storage/logs/laravel.log & // (추천) 라라벨 로그에 같이 처리할 경우

nohup php artisan queue:listen &
nohup php artisan queue:listen >> storage/logs/laravel.log & // 라라벨 로그에 같이 처리할 경우

nohup 다루기 참조

3. Job 보강

위에서는 IndexNow 라는 job 파일을 생성했는데 다시 그 부분에 대해서 추가하도록 하겠습니다.
4.에서 IndexNow::dispatch($request->somedata);를 사용하였는데 이 IndexNow, somedata 는 jobs라는 테이블에 저장만 되는 것입니다.
실행은 php artisan queue:work 를 통해서 되는데 이때 queue:work 는 jobs 테이블을 참조하여 displayName 여기서는 IndexNow 를 호출하고 IndexNow construct 에 somedata 를 전달한 후 handle()를 실행하게 됩니다.

<?php
..........
class IndexNow implements ShouldQueue
{
  protected $somedata;
  public function __construct($somedata)
  {
    $this->somedata = $somedata;
  }

  /**
   * Execute the job.
   *
   * @return void
   */
  public function handle()
  {
    $this->bingIndexNow($this->somedata);
  }

   private function bingIndexNow($path) {
    $client = new \GuzzleHttp\Client();
    $endpoint = 'api.indexnow.org';
    
    $urlList = [env('APP_URL').'/'.$path];
    $query = [
      'host'=> env('APP_URL'),
      'key'=> env('INDEXNOW_KEY'),
      'keyLocation'=>env('APP_URL').'/'.env('INDEXNOW_KEY').'.txt',
      'urlList'=> $urlList
    ];

    $response = $client->request('POST', $endpoint, ['form_params' => $query]); // GET 을 사용할 경우 'query' 변수에 담아 보낸다.
    Log::info('Bing IndexNow Status Code');
    Log::info($response->getStatusCode());
  }
}

평점을 남겨주세요
평점 : 5.0
총 투표수 : 1

질문 및 답글