[Laravel] Queues를 이용한 비동기 프로그래밍
[Laravel] Queues를 이용한 비동기 프로그래밍
라라벨은 queues를 활용하면 대량의 메일 발송이나 시간이 많이 걸리는 프로세스등을 백그라운드로 처리할 수 있다.
설정파일은 config/queue.php 파일이다
지원하는 queue로는 sync, database, beanstalkd, sqs, redis 등등이 있으며
여기서는 database를 활용한 큐를 처리하는 예제를 설명드릴텐데 전반적인 그림은 아래와 같다.
- 사용자가 프로그램실행 > jobs 테이블에 queue 저장 > queue:work가 저장된 queue 처리
라라벨의 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) 설정
QUEUE_CONNECTION=database
config/queue.php 파일을 보면 default가 sync로 설정되어 있고 다양한 connection이 있음을 확인 할 수 있다
저는 database를 사용할 예정이므로 .env 파일에서 QUEUE_CONNECTION=database 로 변경하였습니다.
아래 database에서 table을 위에서 만든 jobs를 사용하고 있음을 알 수 있습니다.
'default' => env('QUEUE_CONNECTION', 'sync'),
'connections' => [
..........
'database' => [
'driver' => 'database',
'table' => 'jobs',
..........,
],
],
3. Job 제작
php artisan make:job FirstJob
명령을 실행하면 app/Jobs 폴더안에 FirstJob.php파일이 생성된다.
이곳에서 우리는 필요한 프로그램을 하여야 하는데 먼저 기본적인 모양만 보여드리기 위해 생성된 파일 그대로를 나열하였습니다.
..........
class FirstJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
..........
public function __construct()
{
}
public function handle()
{
}
}
4. Dispatch the Job
dispatch 를 사용하면 jobs 테이블에 queue가 저장된다.
use App\Jobs\FirstJob;
..........
class MyController extends Controller
{
..........
public function MyMethod(Request $request) {
FirstJob::dispatch($request->somedata);
..........
}
}
위와 같이 Job을 생성하여 처리하는 방식외에도 Notificaion, Mail등에서는 자체적으로 queue에 저장하는 방식을 제공한다.
Notification의 경우는 implements ShouldQueue로서 지원하고
Mail의 경우 ->queue(new..) 를 사용함으로 가능한다. 각각에 대한 예제는 각 chapter에서 다시 설명 드리겠습니다.
5. jobs 테이블에 데이타가 들어갔는지 확인
위의 코드를 실행하면 위에서 만든 jobs 테이블에 데이타가 들어가게 됩니다.
id : 1
queue: default
payload: {"uuid":"f6b8dd54-5639-4a47-bcb9-b8cea590806c","displayName":"App\\Jobs\\FirstJob","job":"Illuminate\\Queue\\CallQueuedHandler@call","maxTries":null,"maxExceptions":null,"failOnTimeout":false,"backoff":null,"timeout":null,"retryUntil":null,"data":{"commandName":"App\\Jobs\\FirstJob","command":"O:17:\"App\\Jobs\\FirstJob\":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 테이블에 들어가 있는 데이타는 라라벨의 artisan 명령어인 queue:work 혹은 queue:listen으로 실행이 되며 실행후 현재 jobs테이블의 데이타는 사라진다. 실패하면 failed_jobs라는 테이블에 실패한 처리내용이 들어가게 됩니다.
queue:work vs queue:listen
work 는 Job의 프로그램이 변경되면 다시 실행하여야 정삭으로 작동되지만 listen은 바로 적용된다.
- work: 안정적인 운영이 필요로 하는 경우(주의: work로 실행할 경우 job이 변경되면 다시 재시작하여야 한다.)
- listen: 변경된 소스를 실시간 적용(개발중인 경우 이 것이 유리)
php artisan queue:listen // php artisan queue:work
> [2024-03-21 18:03:39][1] Processing: App\Jobs\FirstJob
> [2024-03-21 18:03:39][1] Processed: App\Jobs\FirstJob
위의 방식은 터미널을 닫으면 실행되지 않으므로 아래와 같이 백그라운드로 실행하여야 한다.
아래는 supervisor 혹은 nohup을 사용할때의 각각의 방식에 대해 간략하게 설명드립니다.
supervisor를 이용한 백그라운드 실행
conf 파일 생성
/etc/supervisor/conf.d 에서 laravel-worker.conf 파일을 생성후 아래와 같이 입력합니다.
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
; command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600
command=php /home/forge/app.com/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
;user=forge // 실제 리눅스의 계정을 입력
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
stopwaitsecs=3600
sudo supervisorctl reread // 위에 작성된 conf 파일을 리로드
sudo supervisorctl update // 위에 작성된 conf 파일을 업데이트
sudo supervisorctl start "laravel-worker:*" // conf 파일 실행
sudo supervisorctl restart "laravel-worker:*" //stop 이후 start 실행
sudo supervisorctl stop "laravel-worker:*" // 종료시
sudo supervisorctl status // 상태확인
nohup을 이용한 백그라운드 실행
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 & // 라라벨 로그에 같이 처리할 경우
7. Job 파일
4.에서 FirstJob::dispatch($request->somedata);를 호출하면 아래처럼 처리하여야 합니다.
..........
class FirstJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
..........
private $somedata;
public function __construct($somedata) // FirstJob::dispatch($request->somedata) 를 받음
{
$this->somedata = $somedata;
}
public function handle()
{
// $this->somedata 를 이용하여 이곳에서 프로그램 처리
}
}