Laravel Queue Rabbitmq
vladimir-yuldashev/laravel-queue-rabbitmq
Laravel Queue용 RabbitMQ 드라이버입니다. Laravel Horizon을 지원합니다.
Laravel Queue RabbitMQ 사용 가이드
이 패키지는 Laravel의 큐 시스템에서 RabbitMQ를 드라이버로 사용할 수 있도록 지원합니다. Redis나 데이터베이스 드라이버 대신, 메시지 브로커로 검증된 RabbitMQ를 큐 백엔드로 활용하고 싶을 때 유용합니다.
지원 정책
최신 버전에만 새로운 기능이 추가됩니다. 버그 수정은 아래 정책에 따라 제공됩니다.
| 패키지 버전 | Laravel 버전 | 버그 수정 지원 종료일 | |
|---|---|---|---|
| 13 | 9 | 2023년 8월 8일 | Documentation |
설치
Composer를 통해 패키지를 설치합니다.
composer require vladimir-yuldashev/laravel-queue-rabbitmq
이 패키지는 별도 설정 없이 자동으로 등록됩니다.
기본 설정
config/queue.php 파일에 RabbitMQ 커넥션을 추가합니다.
NOTE
아래 설정은 RabbitMQ 커넥션/드라이버가 동작하기 위한 최소 구성입니다.
'connections' => [
// ...
'rabbitmq' => [
'driver' => 'rabbitmq',
'hosts' => [
[
'host' => env('RABBITMQ_HOST', '127.0.0.1'),
'port' => env('RABBITMQ_PORT', 5672),
'user' => env('RABBITMQ_USER', 'guest'),
'password' => env('RABBITMQ_PASSWORD', 'guest'),
'vhost' => env('RABBITMQ_VHOST', '/'),
],
// ...
],
// ...
],
// ...
],로컬 개발 환경에서는 Docker로 RabbitMQ를 띄우는 경우가 많습니다. 관리 콘솔까지 함께 사용하려면
rabbitmq:3-management이미지를 사용하면http://localhost:15672에서 큐 상태를 시각적으로 확인할 수 있어 편리합니다.
큐 옵션 설정 (선택 사항)
커넥션 설정에 큐 옵션을 추가로 지정할 수 있습니다. 이 커넥션으로 생성되는 모든 큐는 여기서 지정한 속성을 갖게 됩니다.
지연된(delayed) 메시지에 우선순위를 부여하고 싶다면 아래와 같이 옵션을 추가합니다.
queue_max_priority를 생략하면 사용 시 기본값 2가 적용됩니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'options' => [
'queue' => [
// ...
'prioritize_delayed' => false,
'queue_max_priority' => 10,
],
],
],
// ...
],라우팅 키(routing-key)를 사용해 익스체인지(exchange)로 메시지를 발행하고 싶다면 다음과 같이 옵션을 추가합니다.
exchange를 생략하면 RabbitMQ는 라우팅 키에 대해amq.direct익스체인지를 사용합니다.routing-key를 생략하면 기본적으로queue이름이 라우팅 키로 사용됩니다.- 라우팅 키에
%s를 사용하면 큐 이름으로 치환됩니다.
NOTE
익스체인지와 라우팅 키를 함께 사용하는 경우, 큐와 바인딩(binding)은 직접 생성해야 하는 경우가 대부분입니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'options' => [
'queue' => [
// ...
'exchange' => 'application-x',
'exchange_type' => 'topic',
'exchange_routing_key' => '',
],
],
],
// ...
],Laravel은 기본적으로 실패한 Job을 데이터베이스에 저장합니다. 하지만 경우에 따라 실패한 메시지를 다른 프로세스에서 별도로 처리하고 싶을 수 있습니다. 이때는 실패한 메시지를 특정 익스체인지나 큐로 재전달하도록 RabbitMQ에 지시할 수 있습니다.
exchange를 생략하면 RabbitMQ는 라우팅 키에 대해amq.direct익스체인지를 사용합니다.routing-key를 생략하면 기본적으로 큐 이름 뒤에.failed가 붙은 값이 라우팅 키로 사용됩니다.- 라우팅 키에
%s를 사용하면 큐 이름으로 치환됩니다.
NOTE
실패 Job용 익스체인지와 라우팅 키를 사용할 경우, 해당 익스체인지/큐와 바인딩은 직접 생성해야 하는 경우가 대부분입니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'options' => [
'queue' => [
// ...
'reroute_failed' => true,
'failed_exchange' => 'failed-exchange',
'failed_routing_key' => 'application-x.%s',
],
],
],
// ...
],Horizon 지원
8.0 버전부터 이 패키지는 Laravel Horizon을 기본적으로 지원합니다. 먼저 Horizon을 설치한 뒤 RABBITMQ_WORKER 값을 horizon으로 설정하세요.
Horizon은 워커가 발생시키는 이벤트를 기반으로 동작합니다. 이 이벤트들은 메시지/Job에 어떤 작업이 이루어졌는지를 Horizon에 알려주는 역할을 합니다.
이 라이브러리는 Horizon을 지원하지만, 설정 파일에서 Horizon과 호환되는 Queue API를 사용하도록 Laravel에 명시적으로 알려주어야 합니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
/* Laravel Horizon을 사용하려면 "horizon"으로 설정하세요. */
'worker' => env('RABBITMQ_WORKER', 'default'),
],
// ...
],커스텀 RabbitMQJob 클래스 사용하기
때로는 다른 애플리케이션이 발행한 메시지를 처리해야 하는 경우가 있습니다. 이런 메시지들은 대부분 Laravel의 Job 페이로드 스키마를 따르지 않습니다. 문제는 이런 메시지를 받았을 때 Laravel 워커가 실제로 실행할 Job이나 클래스를 판단할 수 없다는 점입니다.
이럴 때는 기본 제공되는 RabbitMQJob::class를 확장하고, 큐 커넥션 설정에서 직접 만든 클래스를 지정하면 됩니다. 설정에 job 키로 커스텀 클래스명을 지정하면, 브로커에서 가져온 모든 메시지가 해당 클래스로 래핑됩니다.
설정 예시:
'connections' => [
// ...
'rabbitmq' => [
// ...
'options' => [
'queue' => [
// ...
'job' => \App\Queue\Jobs\RabbitMQJob::class,
],
],
],
// ...
],커스텀 Job 클래스 예시:
<?php
namespace App\Queue\Jobs;
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob as BaseJob;
class RabbitMQJob extends BaseJob
{
/**
* Job을 실행합니다.
*
* @return void
*/
public function fire()
{
$payload = $this->payload();
$class = WhatheverClassNameToExecute::class;
$method = 'handle';
($this->instance = $this->resolve($class))->{$method}($this, $payload);
$this->delete();
}
}
또는 페이로드에 추가 속성을 넣고 싶다면 다음과 같이 작성할 수 있습니다.
<?php
namespace App\Queue\Jobs;
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob as BaseJob;
class RabbitMQJob extends BaseJob
{
/**
* Job의 디코딩된 본문을 가져옵니다.
*
* @return array
*/
public function payload()
{
return [
'job' => 'WhatheverFullyQualifiedClassNameToExecute@handle',
'data' => json_decode($this->getRawBody(), true)
];
}
}JSON 형식이 아니거나 job 키가 없는 원시(raw) 메시지를 처리하고 싶다면, getName 메서드에 대한 스텁을 추가해야 합니다.
<?php
namespace App\Queue\Jobs;
use Illuminate\Support\Facades\Log;
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob as BaseJob;
class RabbitMQJob extends BaseJob
{
public function fire()
{
$anyMessage = $this->getRawBody();
Log::info($anyMessage);
$this->delete();
}
public function getName()
{
return '';
}
}커스텀 Connection 클래스 사용하기
기본 제공되는 PhpAmqpLib\Connection\AMQPStreamConnection::class 또는 PhpAmqpLib\Connection\AMQPSLLConnection::class를 확장하여, 커넥션 설정에서 직접 만든 클래스를 지정할 수 있습니다. 설정에 connection 키로 커스텀 클래스명을 지정하면, 모든 커넥션이 해당 클래스를 사용하게 됩니다.
설정 예시:
'connections' => [
// ...
'rabbitmq' => [
// ...
'connection' = > \App\Queue\Connection\MyRabbitMQConnection::class,
],
// ...
],커스텀 Worker 클래스 사용하기
VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue를 확장하면 직접 만든 RabbitMQQueue::class를 사용할 수 있습니다. 이후 RABBITMQ_WORKER 값을 \App\Queue\RabbitMQQueue::class로 설정하여 Laravel이 이 클래스를 사용하도록 지정합니다.
NOTE
Worker 클래스는 반드시 VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue를 상속해야 합니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
/* 커스텀 클래스를 사용하려면 해당 클래스로 지정하세요. */
'worker' => \App\Queue\RabbitMQQueue::class,
],
// ...
],<?php
namespace App\Queue;
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue;
class RabbitMQQueue extends BaseRabbitMQQueue
{
// ...
}예시: 재연결(reconnect) 구현하기
연결이 끊어졌을 때 RabbitMQ에 다시 연결하고 싶다면, publishBasic과 createChannel 등의 메서드를 오버라이드할 수 있습니다.
NOTE
아래 코드는 모범 사례(best practice)라기보다 구현 방식을 보여주기 위한 예시입니다.
<?php
namespace App\Queue;
use PhpAmqpLib\Exception\AMQPChannelClosedException;
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue;
class RabbitMQQueue extends BaseRabbitMQQueue
{
protected function publishBasic($msg, $exchange = '', $destination = '', $mandatory = false, $immediate = false, $ticket = null): void
{
try {
parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket);
} catch (AMQPConnectionClosedException|AMQPChannelClosedException) {
$this->reconnect();
parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket);
}
}
protected function publishBatch($jobs, $data = '', $queue = null): void
{
try {
parent::publishBatch($jobs, $data, $queue);
} catch (AMQPConnectionClosedException|AMQPChannelClosedException) {
$this->reconnect();
parent::publishBatch($jobs, $data, $queue);
}
}
protected function createChannel(): AMQPChannel
{
try {
return parent::createChannel();
} catch (AMQPConnectionClosedException) {
$this->reconnect();
return parent::createChannel();
}
}
}기본 큐(Default Queue)
Laravel에서 큐가 별도로 지정되지 않은 경우, 커넥션은 기본값인 default 큐를 사용합니다. 커넥션 설정에 아래와 같은 파라미터를 추가하면 기본 큐 이름을 변경할 수 있습니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'queue' => env('RABBITMQ_QUEUE', 'default'),
],
// ...
],Heartbeat
기본적으로 커넥션은 heartbeat 설정값 0으로 생성됩니다. 설정을 변경하면 heartbeat 값을 조정할 수 있습니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'options' => [
// ...
'heartbeat' => 10,
],
],
// ...
],SSL 보안 연결
RabbitMQ 서버와 보안 연결(SSL)이 필요하다면, 아래와 같은 추가 설정 옵션이 필요합니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'secure' = > true,
'options' => [
// ...
'ssl_options' => [
'cafile' => env('RABBITMQ_SSL_CAFILE', null),
'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null),
'local_key' => env('RABBITMQ_SSL_LOCALKEY', null),
'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true),
'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null),
],
],
],
// ...
],데이터베이스 커밋 이후 이벤트 발생
데이터베이스 트랜잭션의 모든 커밋이 완료된 이후에 Laravel 워커가 이벤트를 발생시키도록 지정할 수 있습니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'after_commit' => true,
],
// ...
],지연 연결(Lazy Connection)
기본적으로 커넥션은 지연(lazy) 방식으로 생성됩니다. 즉, 실제로 필요한 시점이 되어야 연결이 맺어집니다. 이 동작을 원치 않는다면 다음과 같이 설정을 끌 수 있습니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'lazy' = > false,
],
// ...
],네트워크 프로토콜
기본적으로 커넥션에는 tcp 프로토콜이 사용됩니다. 다른 프로토콜을 사용하고 싶다면 설정 옵션에 값을 추가하면 됩니다. 사용 가능한 프로토콜: tcp, ssl, tls
'connections' => [
// ...
'rabbitmq' => [
// ...
'network_protocol' => 'tcp',
],
// ...
],네트워크 타임아웃
네트워크 타임아웃은 옵션 파라미터로 설정할 수 있습니다. 모든 값은 초 단위의 실수(float)이며, 0으로 설정하면 타임아웃 없음(무한대)을 의미할 수 있습니다. 아래 예시는 기본값을 보여줍니다.
'connections' => [
// ...
'rabbitmq' => [
// ...
'options' => [
// ...
'connection_timeout' => 3.0,
'read_timeout' => 3.0,
'write_timeout' => 3.0,
'channel_rpc_timeout' => 0.0,
],
],
// ...
],Octane 지원
13.3.0 버전부터 이 패키지는 Laravel Octane을 기본적으로 지원합니다. 먼저 Octane을 설치한 뒤, Octane 설정 파일에서 rabbitmq 커넥션을 warm(사전 로드) 대상에 포함하는 것을 잊지 마세요.
참고: https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/460#issuecomment-1469851667
Laravel에서 사용하기
설정을 마쳤다면 Laravel의 Queue API를 그대로 사용하면 됩니다. 다른 큐 드라이버를 사용해 본 경험이 있다면 추가로 변경할 것은 없습니다. Queue API 사용법을 잘 모른다면 Laravel 공식 문서를 참고하세요: http://laravel.com/docs/queues
Lumen에서 사용하기
Lumen에서는 bootstrap/app.php에 서비스 프로바이더를 직접 등록해야 합니다.
$app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class);메시지 소비하기
메시지를 소비(consume)하는 방법은 두 가지가 있습니다.
-
Laravel 기본 제공 명령어인
queue:work. 이 명령어는 내부적으로basic_get을 사용합니다. 여러 개의 큐를 동시에 소비해야 한다면 이 방식을 사용하세요. -
이 패키지가 제공하는
rabbitmq:consume명령어. 이 명령어는basic_consume을 사용하며,basic_get방식보다 약 2배 더 높은 성능을 보입니다. 다만 여러 큐를 동시에 소비하는 기능은 지원하지 않습니다.
실무 팁: 큐가 하나뿐이고 처리량이 중요한 서비스라면
rabbitmq:consume을, 여러 큐를 유연하게 운영해야 하는 환경이라면queue:work를 선택하는 것이 일반적입니다.
테스트
docker-compose를 사용해 RabbitMQ 테스트 환경을 구성합니다.
docker compose up -d테스트 스위트는 다음 명령어로 실행할 수 있습니다.
# 스타일 테스트와 유닛 테스트를 모두 실행composer test# 스타일 테스트만 실행composer test:style# 유닛 테스트만 실행composer test:unit스타일 테스트에서 오류가 발생했다면, 아래 명령어로 대부분의 문제를 자동으로 수정할 수 있습니다.
composer fix:style기여하기
버그를 발견하거나 이슈를 등록하는 방식으로 이 패키지에 기여할 수 있습니다. 이슈나 풀 리퀘스트를 작성할 때는 대상 패키지 버전을 함께 명시해 주세요. (예: [5.2] 지연 Job에서 치명적 오류 발생)