Using RabbitMQ with Queues in Laravel

RabbitMQ is a widely used message broker for asynchronous task processing. In Laravel, we can integrate it as a queue driver to improve application performance and scalability. Setting Up RabbitMQ in Laravel Installing the Package Laravel does not have native support for RabbitMQ, so we use a third-party package like vladimir-yuldashev/laravel-queue-rabbitmq. composer require vladimir-yuldashev/laravel-queue-rabbitmq Configuring config/queue.php Add the RabbitMQ configuration:

Mar 26, 2025 - 21:30
 0
Using RabbitMQ with Queues in Laravel

RabbitMQ is a widely used message broker for asynchronous task processing. In Laravel, we can integrate it as a queue driver to improve application performance and scalability.

Setting Up RabbitMQ in Laravel

Installing the Package

Laravel does not have native support for RabbitMQ, so we use a third-party package like vladimir-yuldashev/laravel-queue-rabbitmq.

composer require vladimir-yuldashev/laravel-queue-rabbitmq

Configuring config/queue.php

Add the RabbitMQ configuration:



'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', '/'),
            ],
        ],
        'queue' => env('RABBITMQ_QUEUE', 'default'),
        'options' => [
            'exchange' => [
                'name' => env('RABBITMQ_EXCHANGE_NAME', 'default'),
                'type' => env('RABBITMQ_EXCHANGE_TYPE', 'direct'),
            ],
        ],
    ],
],

Setting Env Vars

Add to .env:

QUEUE_CONNECTION=rabbitmq
RABBITMQ_HOST=127.0.0.1
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_VHOST=/
RABBITMQ_QUEUE=default

Creating and Dispatching Jobs

php artisan make:job ProcessPayment

Edit app/Jobs/ProcessPayment.php



namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique; 
use Illuminate\Contracts\Queue\ShouldBeQueued;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Models\Order;
use App\Models\Inventory;
use App\Services\PaymentService;
use App\Exceptions\PaymentFailedException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ProcessPayment implements ShouldBeQueued
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $tries = 3; // The maximum number of times this job will be attempted
    public $backoff = [10, 30, 60]; // The delay in seconds before retrying the job after a failure, increasing with each attempt

    protected $paymentService;

    // Constructor to inject the PaymentService dependency
    public function __construct(PaymentService $paymentService)
    {
        $this->paymentService = $paymentService;
    }

    // This method contains the main logic to process the payment
    public function handle()
    {
        try {
            // Step 1: Process the payment using the injected PaymentService
            $paymentSuccess = $this->paymentService->process();

            // If the payment was not successful, throw a specific exception
            if (!$paymentSuccess) {
                throw new PaymentFailedException('Payment processing failed.');
            }

            // Step 2 & 3: Create the order and deduct inventory within a database transaction to ensure data consistency
            DB::transaction(function () {
                // Check if an order with the same criteria already exists to ensure idempotency
                $order = Order::firstOrCreate([
                    'user_id' => 1,
                    // Add other unique criteria for identifying existing orders
                ], [
                    'status' => 'paid',
                    'total' => 100.00,
                ]);

                // Deduct the item quantity from the inventory (consider using optimistic/pessimistic locking for concurrency)
                Inventory::where('product_id', 1)->decrement('quantity', 1);
            });

        } catch (PaymentFailedException $e) {
            // Specific handling for payment failures
            Log::warning('Payment failure while processing order: ' . $e->getMessage());
            throw $e; // Re-throw the exception to trigger the retry mechanism
        } catch (Exception $e) {
            // Handling for unexpected errors
            Log::error('Unexpected error while processing order: ' . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
            throw $e; // Re-throw the exception to trigger the retry mechanism
        }
    }

    // This method is executed if the job fails after all retry attempts
    public function failed(Exception $exception)
    {
        // Logic to be executed when the job completely fails
        Log::critical('Critical failure while processing order after multiple retries: ' . $exception->getMessage(), ['trace' => $exception->getTraceAsString()]);
        // Consider sending notifications to support teams or implementing other failure handling strategies
    }
}

Dispatching a Job

use App\Jobs\ProcessPayment;

ProcessPayment::dispatch();

Running the worker

php artisan queue:work rabbitmq

Conclusion

By integrating RabbitMQ with Laravel queues, you can significantly enhance your application's performance by offloading time-consuming tasks to background processing. This results in a more responsive user experience and improved system scalability.

With features like automatic retries and message durability, RabbitMQ ensures that tasks are processed reliably even in case of failures. Using Laravel's queue system with RabbitMQ allows you to build robust and efficient workflows, making it an excellent choice for large-scale applications requiring asynchronous processing.

For more details, refer to the official documentation: