9.9 KiB
Laravel RabbitMQ Events
A package for working with RabbitMQ messages as Laravel events.
Automatically serialize and publish events to RabbitMQ, and consume messages from queues by converting them back into events.
Requirements
- PHP 8.1+
- Laravel 10, 11 or 12
- RabbitMQ server
Installation
- Install via Composer:
composer require diffhead/laravel-rabbitmq
- Register package's service provider
// app/providers.php
return [
\Diffhead\PHP\LaravelRabbitMQ\ServiceProvider::class
];
Usage
1. Creating regular events for publishing to a RabbitMQ
Create an event that implements the Broadcast interface. Broadcast interface
extends JsonSerializable then your event should implements jsonSerialize method.
namespace App\Events;
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
use Diffhead\PHP\LaravelRabbitMQ\Trait\BroadcastEvent;
use Illuminate\Foundation\Events\Dispatchable;
class UserCreated implements Broadcast
{
use Dispatchable, BroadcastEvent;
public function __construct(
public int $userId,
public string $email,
public string $name,
) {}
public function jsonSerialize(): array
{
return [
'userId' => $this->userId,
'email' => $this->email,
'name' => $this->name,
];
}
}
Event Parameters
When implementing the Broadcast interface, you must define the following methods
for targeting RabbitMQ message:
getConnection(): string— connection name defined in configurationgetQueue(): string— Queue namegetExchange(): string— Exchange namegetExchangeType(): string— Exchange type (direct, topic, fanout, headers)getExchangeIsDefault(): bool— Whether to use the default exchangegetRoutingKey(): string— Routing key for the message
Using the BroadcastEvent trait
The BroadcastEvent trait provides implementations of all methods using default parameters from configuration. You can directly override method's return value for
customizing concrete event or override this behaviour globally by changing
following env variables:
RABBITMQ_EVENT_CONNECTION=defaultRABBITMQ_EVENT_QUEUE=defaultRABBITMQ_EVENT_EXCHANGE=amq.directRABBITMQ_EVENT_EXCHANGE_TYPE=directRABBITMQ_EVENT_EXCHANGE_IS_DEFAULT=trueRABBITMQ_EVENT_ROUTING_KEY=
namespace App\Events;
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
use Diffhead\PHP\LaravelRabbitMQ\Trait\BroadcastEvent;
class UserCreated implements Broadcast
{
use BroadcastEvent;
}
If you need special parameters for a specific event, override the necessary methods:
namespace App\Events;
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
use Diffhead\PHP\LaravelRabbitMQ\Trait\BroadcastEvent;
class CriticalAlert implements Broadcast
{
use BroadcastEvent;
public function getRoutingKey(): string
{
return 'alert.critical';
}
public function getExchange(): string
{
return 'alerts.topic';
}
public function getExchangeType(): string
{
return 'topic';
}
}
Publishing Events
Events are automatically published when dispatched.
\Diffhead\PHP\LaravelRabbitMQ\Listener\PublishEvent
This listener implements ShouldQueue interface then
you can setup his behaviour by chaning following env variables:
RABBITMQ_EVENT_PUBLISHING_CONNECTION=syncRABBITMQ_EVENT_PUBLISHING_QUEUE=default
Example:
App\Events\UserCreated::dispatch(
userId: 1,
email: 'user@example.com',
name: 'John Doe'
);
2. Consuming Messages from RabbitMQ
Map events
Add regular laravel's application event classes mapping to
RabbitMQ queues and routing keys using rabbitmq.event.map section.
Event should implement \Diffhead\PHP\LaravelRabbitMQ\Interface\Event
with no methods if you are using default library mapper.
// config/rabbitmq.php
'map' => [
\App\Events\User\UserCreated::class => [
'queues' => ['portal.users'],
'routing_keys' => ['user.created'],
],
\App\Events\Meeting\MeetingCreated::class => [
'queues' => ['portal.meetings'],
'routing_keys' => ['meeting.created'],
],
],
You can set empty array for matching event of all queues or routing keys.
Follow this example for mapping event with amq.direct exchange which not
uses routing keys:
// config/rabbitmq.php
'map' => [
\App\Events\User\UserCreated::class => [
'queues' => ['portal.users'],
'routing_keys' => [],
],
],
Consume RabbitMQ queues
Use the rabbitmq:consume command to listen for messages.
Consumer will listen rabbitmq bus and emit mapped event from as regular laravel event.
#####################################################################################
#
# Has following options:
#
# --connection=default - Connection name from config
# --queue=default - Queue
# --exchange=amq.direct - Exchange name
# --exchange-type=direct - Exchange type
# --exchange-is-default - Exchange is default, required for default exchanges
# --routing-key=user.* - Listen routing keys, required for topic exchanges
# --tag=myconsumer - Consumer tag for rabbitmq
#
#####################################################################################
php artisan rabbitmq:consume
Full Consumer Startup Example
php artisan rabbitmq:consume \
--connection=default \
--queue=service.users \
--exchange=amq.direct \
--exchange-type=direct \
--routing-key=user.* \
--tag=service-users-consumer
Handling Received Events
When a message is received from RabbitMQ, it is automatically deserialized and dispatched as a Laravel event. You can listen to these events normally:
namespace App\Listeners;
use App\Events\UserCreated;
use Illuminate\Support\Log;
class LogUserCreation
{
public function handle(UserCreated $event): void
{
Log::info("User created: {$event->email}");
}
}
Register the listener in app/Providers/EventServiceProvider.php:
protected $listen = [
\App\Events\UserCreated::class => [
\App\Listeners\LogUserCreation::class,
],
];
or using Laravel's \Illuminate\Support\Facades\Event facade.
3. Configure and customize logics
Serialization
The package does serialization/deserialization of message data via following interfaces:
\Diffhead\PHP\LaravelRabbitMQ\Interface\Serializer\Diffhead\PHP\LaravelRabbitMQ\Interface\Unserializer
By default implementations library interacts with JSON data.
Custom Serialization
You can use your own serialization classes by implementing interfaces and overriding following configuration entities:
'message' => [
'serializer' => \App\Services\CustomSerializer::class,
'unserializer' => \App\Services\CustomUnserializer::class,
],
Custom mapping
You can use your own mapping logic by implementing EventMapper
interface and overriding the following configuration entity:
'event' => [
'mapper' => \App\Services\CustomEventMapper::class,
]
Configuration
Publish the configuration file if it not exists:
php artisan vendor:publish --provider="Diffhead\PHP\LaravelRabbitMQ\ServiceProvider"
Used environment variables:
# RabbitMQ Connection
RABBITMQ_HOST=localhost
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_VHOST=/
# Events publishing listener parameters
RABBITMQ_EVENT_PUBLISHING_CONNECTION=sync
RABBITMQ_EVENT_PUBLISHING_QUEUE=default
# Default event parameters
RABBITMQ_EVENT_CONNECTION=default
RABBITMQ_EVENT_QUEUE=default
RABBITMQ_EVENT_EXCHANGE=amq.direct
RABBITMQ_EVENT_EXCHANGE_TYPE=direct
RABBITMQ_EVENT_EXCHANGE_IS_DEFAULT=true
RABBITMQ_EVENT_ROUTING_KEY=
Example of config/rabbitmq.php
// config/rabbitmq.php
return [
'connections' => [
'default' => [
'host' => env('RABBITMQ_HOST', 'localhost'),
'port' => env('RABBITMQ_PORT', 5672),
'user' => env('RABBITMQ_USER', 'guest'),
'password' => env('RABBITMQ_PASSWORD', 'guest'),
'vhost' => env('RABBITMQ_VHOST', '/'),
],
'secondary' => [
'host' => env('RABBITMQ_SECONDARY_HOST', 'localhost'),
'port' => env('RABBITMQ_SECONDARY_PORT', 5672),
'user' => env('RABBITMQ_SECONDARY_USER', 'guest'),
'password' => env('RABBITMQ_SECONDARY_PASSWORD', 'guest'),
'vhost' => env('RABBITMQ_SECONDARY_VHOST', '/'),
]
],
'message' => [
'serializer' => \Diffhead\PHP\LaravelRabbitMQ\Service\Serializer::class,
'unserializer' => \Diffhead\PHP\LaravelRabbitMQ\Service\Unserializer::class,
],
'event' => [
'defaults' => [
'connection' => env('RABBITMQ_EVENT_CONNECTION', 'default'),
'queue' => env('RABBITMQ_EVENT_QUEUE', 'default'),
'exchange' => env('RABBITMQ_EVENT_EXCHANGE', 'amq.direct'),
'exchange_type' => env('RABBITMQ_EVENT_EXCHANGE_TYPE', 'direct'),
'exchange_is_default' => (bool) env('RABBITMQ_EVENT_EXCHANGE_IS_DEFAULT', true),
'routing_key' => env('RABBITMQ_EVENT_ROUTING_KEY', ''),
],
'publishing' => [
'connection' => env('RABBITMQ_EVENT_PUBLISHING_CONNECTION', 'sync'),
'queue' => env('RABBITMQ_EVENT_PUBLISHING_QUEUE', 'default'),
],
'mapper' => \Diffhead\PHP\LaravelRabbitMQ\Service\EventMapper::class,
'map' => [
/**
* Map events to queues and routing keys
*/
\App\Events\User\UserCreated::class => [
'queues' => ['portal.users'],
'routing_keys' => ['user.created'],
],
\App\Events\Meeting\MeetingCreated::class => [
'queues' => ['portal.meetings'],
'routing_keys' => ['meeting.created'],
],
],
]
];