Utilizzare Symfony Messenger per gestire code di messaggi in Symfony

Maico Orazio
weBeetle
Published in
5 min readMay 3, 2024

Symfony, il famoso framework PHP, offre una vasta gamma di strumenti e componenti potenti per sviluppatori di tutti i livelli. Uno di questi strumenti è Symfony Messenger, un bundle che semplifica notevolmente la gestione dei messaggi all’interno delle applicazioni Symfony.

Cos’è Symfony Messenger?

Symfony Messenger è un bundle che fornisce tutto ciò di cui hai bisogno per consumare i messaggi provenienti da una coda di messaggi. Questo approccio è estremamente utile per migliorare le prestazioni delle nostre applicazioni, consentendo di separare le attività pesanti e gestirle tramite un lavoratore separato.

Vantaggi dell’accodamento dei messaggi

L’accodamento dei messaggi consente di:

  • Migliorare le prestazioni dell’applicazione;
  • Gestire attività pesanti in modo asincrono;
  • Mantenere un tempo di risposta rapido per gli utenti.

Se non hai familiarità con l’accodamento dei messaggi, ti consiglio di approfondire l’argomento per comprendere appieno i suoi benefici.

Caso d’uso

Per comprendere meglio l’utilità di Symfony Messenger, consideriamo un caso d’uso comune: la gestione degli ordini e delle spedizioni in un’applicazione.

Immaginiamo di dover eseguire diverse attività ogni volta che un ordine viene aggiornato, come:

  • Aggiornare lo stato dell’ordine sul sito web;
  • Inviare una e-mail di conferma all’utente;
  • Magari, inviare una notifica tramite SMS;
  • Eseguire altri script interni.

Tutte queste attività richiedono chiamate a servizi diversi, il che può richiedere molto tempo e il nostro obiettivo è avere il minor tempo di risposta possibile.

Utilizzando Symfony Messenger, potremmo delegare queste attività a un lavoratore separato, migliorando notevolmente le prestazioni complessive dell’applicazione.

Introduzione e installazione

Per iniziare a utilizzare Symfony Messenger, dobbiamo prima installare il pacchetto tramite Composer:

> composer require symfony/messenger

Questo pacchetto fornisce tutto il necessario per creare e gestire messaggi e gestori di messaggi all’interno della nostra applicazione.

Messaggi e Gestori

Il cuore di Symfony Messenger ruota attorno ai messaggi e ai gestori di messaggi. Un messaggio rappresenta i dati che devono essere elaborati, mentre un gestore di messaggi contiene la logica per elaborare quel messaggio.

Ad esempio, supponiamo di dover inviare un’e-mail di conferma a un utente che si è appena registrato sul nostro sito. Creeremo un messaggio chiamato SendRegistrationEmailMessage che conterrà l’ID dell’utente.

<?php

namespace App\Message;

class SendRegistrationEmailMessage
{
public function __construct(
private readonly int $userId
) {}

public function getUserId(): int
{
return $this->userId;
}
}

Invece di eseguire tutto nel momento esatto in cui viene inviata la richiesta di registrazione, invieremo un messaggio in coda, contenente l’ID dell’utente.
In questo modo, il nostro controller non avrà più motivo di lanciare un’eccezione e potremmo inviare una risposta “OK” (200) molto più velocemente.

Successivamente, creeremo un gestore di messaggi chiamato SendRegistraionEmailMessageHandler che recupererà l’utente dall’ID e invierà l’e-mail di conferma.

<?php

namespace App\MessageHandler;

use App\Entity\User;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
class SendRegistrationEmailMessageHandler
{
public function __construct(
private readonly EntityManagerInterface $entityManager
) {}

public function __invoke(SendRegistrationEmailMessage $message): void
{
$user = $this->entityManager->getRepository(User::class)->find($message->getUserId());

if (!$user instanceOf User) {
// Handle entity not found case
}

// Send the email
}
}

Symfony Messenger semplifica questo processo, consentendoci di concentrarci sulla logica di business senza preoccuparci dei dettagli di implementazione.

Vediamo come.

Symfony Messenger viene fornito con un attributo PHP #[AsMessageHandler] , in modo che Symfony consideri questo servizio come un gestore di messaggi a cui instradare i messaggi corretti.

Il tipo del messaggio da consumare è quello dichiarato nel metodo __invoke.
Ciò significa che per ogni messaggio, dovremmo creare un gestore di messaggi, con il tipo corretto nel metodo __invoke e l’instradamento sarà gestito automaticamente da Symfony.

Trasporto e instradamento dei messaggi

Symfony Messenger supporta diversi tipi di trasporto:

  • Servizi AMQP (come RabbitMQ);
  • Doctrine (memorizzazione dei messaggi in una tabella SQL);
  • Servizi di cache (come Redis).

Innanzitutto, se intendiamo utilizzare un protocollo AMQP, dobbiamo installare il pacchetto:

> composer require symfony/amqp-messenger

Se intendiamo utilizzare Doctrine, invece bisogna installare quanto segue:

> composer require symfony/doctrine-messenger

Infine, se desideriamo utilizzare Redis come nostro trasporto, questo è il pacchetto che dobbiamo installare:

> composere require symfony/redis-messenger

Se vuoi saperne di più sugli altri trasporti, puoi dare uno sguardo alla relativa documentazione.

Possiamo configurare facilmente il trasporto desiderato nel file di configurazione del pacchetto messenger.yaml, che troveremo in config/packages insieme a tutti gli altri file di configurazione dei pacchetti installati.

La chiave transport contiene tutta la configurazione riguardante la gestione (consumo) dei messaggi.

La chiave failure-transport contiene il nome del trasporto che deve essere utilizzato in caso di problemi (eccezione lanciata durante la gestione).
Se qualcosa si blocca qui, l’analisi dello stack e i dettagli dell’eccezione verranno salvati nel messaggio e potranno essere recuperati in seguito per essere gestiti correttamente.

Possiamo anche configurare il consumatore per eseguire x tentativi prima di inviare un messaggio di errore sulla coda “non riuscita” e gestire priorità diverse per ciascuna coda.

Infine, la parte routing spiega come instradare una determinata istanza del messaggio al trasporto.

Ecco un esempio di come potrebbe essere il nostro file di configurazione del caso riportato sopra:

framework:
messenger:
transports:
registration_email:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
failure_transport: registration_email_failed
retry_strategy:
max_retries: 3
delay: 1000
multiplier: 2
max_delay: 0
options:
exchange:
name: registration_email
queues:
registration_email: ~

registration_email_failed:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
exchange:
name: registration_email_failed
queues:
registration_email_failed: ~

routing:
'App\Message\SendRegistrationEmailMessage': registration_email

Pubblicazione di messaggi

La pubblicazione di un messaggio è semplice come chiamare il servizio MessageBus fornito da Symfony Messenger e passare il messaggio da pubblicare. Il bus si occuperà del resto, instradando il messaggio al gestore appropriato per l’elaborazione.

Ecco un esempio:

<?php

namespace App\Service;

use App\Message\SendEMailRegistrationEmailMessage;

class MyExampleService
{
public function __construct(
private readonly MessageBusInterface $messageBus
) {}

public function doSomething(): void
{
// Any logic...

$message = new SendEMailRegistrationEmailMessage($userId);

# This will dispatch the message to the correct transport
$this->messageBus->dispatch($message);
}
}

Architettura

L’architettura di Symfony Messenger è ben strutturata e prevede l’uso di un editore (controller, servizio, comando, ecc.) che invia un messaggio al bus. Se il bus è sincrono, il messaggio viene consumato direttamente da un gestore. Se il bus è asincrono, il messaggio viene inviato tramite un trasporto a un sistema di code, dove viene elaborato da un lavoratore separato.

Se il trasporto è asincrono, il messaggio dovrà essere serializzato per poter essere interpretato e consumato correttamente dal lavoratore. Symfony supporta nativamente due modalità di serializzazione:

Di default viene utilizzata la serializzazione nativa di PHP, che rappresenta la classe stessa del messaggio.

Se un’altra applicazione consuma la stessa coda di messaggi, potrebbe non avere questa classe, quindi sarà impossibile deserializzare il messaggio. Per tale motivo, utilizzeremo un formato di scambio più tradizionale. Di solito JSON, ma potremmo utilizzare XML, Protobuf o qualsiasi altro linguaggio di serializzazione interoperabile.

Conclusioni

Symfony Messenger è uno strumento potente per la gestione dei messaggi all’interno delle applicazioni Symfony. Con il suo supporto per vari tipi di trasporto e la sua semplice architettura, ci consente di migliorare le prestazioni dell’applicazione e fornire un’esperienza utente migliore.

Spero che questo articolo ti abbia fornito una panoramica completa di Symfony Messenger e ti abbia ispirato ad utilizzarlo nelle tue prossime applicazioni Symfony! Se desideri ricevere aggiornamenti su futuri articoli, non esitare a seguire il mio account su Medium!

Buon lavoro 👨‍💻

--

--

Maico Orazio
weBeetle

Senior Web Application Developer. I'm a software engineer, a passionate coder, and a web developer. I am a fan of technology. #php #symfony #javascript #reactjs