RabbitMQ es uno de los sistemas de colas de mensajes de código abierto más implementado, probablemente junto con Kafka. El ámbito de uso de RabbitMQ es muy extenso y en cualquier tamaño de empresa. RabbitMQ es lo que se conoce como un broker de mensajería.

RabbitMQ admite múltiples protocolos de mensajería, y se puede implementar en diferentes tipos de configuraciones para cumplir con los requisitos de alta disponibilidad y escala. Además se ejecuta en múltiples sistemas operativos y entornos en al nube, tiene una amplia gama de herramientas en los lenguajes más populares.

Índice:

  1. Introducción a RabbitMQ
    1. Creamos una cuenta en CloudAMQP
  2. Instalamos y configuramos en Laravel la conexión a RabbitMQ
    1. adadfad
  3. Creamos los trabajos (jobs) que dispararán el envío de mensajes a RabbitMQ (PRODUCTOR)
  4. (CONSUMIDOR)
  5. Documentación

RABBITMQ ADMIN Y ORDER APP , ACTUALIZACIÓN DE CLIENTES

5.1. Introducción a RabbitMQ

RabbitMQ es un broker de mensajería que permite definir colas de mensajes desde donde las aplicaciones se pueden conectar para transferir publicar y recibir mensajes. Las características principales son:

  • Garantía de entrega
  • Enrutamiento flexible
  • Clusterización
  • Federación
  • Alta disponibilidad
  • Tolerancia a fallos

El funcionamiento es sencillo hablaremos de productores (P) y consumidores (C) el productor se encarga de generar un mensaje que se publica en la cola de RabbitMQ; el consumidor está escuchando los nuevos mensajes recibidos y se encarga de consumirlos suscribiéndose a la cola para recibir los mensajes. A continuación una imagen ilustrativa perteneciente al tutorial de Getting Started de RabbirtMQ:

(P) -> [|||] -> (C)
RabbitMQ flujo de trabajo

Un software puede productor y consumidor al mismo tiempo de múltiples colas de mensajes.

Aplicado a nuestro proyecto, lo primero que vamos a realizar es la transmisión de los nuevos clientes y sus actualizaciones de ADMIN-APP a ORDERS-APP, en este caso:

  • ADMIN-APP: será el productor, generará un mensaje indicando la creación de un cliente.
  • ADMIN-APP: será el productor, generará un mensaje indicando la actualización de un cliente.
  • RABBITMQ: establece los mecanismos para que el producto pueda publicar los mensajes; y que el consumidor pueda suscribirse a los mismos.
  • ORDER-APP

Añadir alguna explicación más.

Para realizar este tutorial teníamos dos opciones crear una máquina Docker con nuestro software RabbitMQ sirviendo o utilizar un software en la nube de RabbitMQ, en nuestro caso obtamos por este segundo caso utilizando CloudAMQP que es un servicio de RabbitMQ as a Service y está genial para empezar ya que nos permite conectarnos gratuitamente para desarrollo.

CloudAMQP la cuenta gratis será suficiente para nuestros desarrollos:

  • Max 100 queues
  • Max 10 000 queued messages
  • Max idle queue time 28 days
  • 1 M Max MSGs/month
  • 20 Max connections
  • Otros planes: Cloud AMQP Plans

5.1.1. Creamos una cuenta en CloudAMQP

Podéis crear una cuenta en Cloud AMQP con vuestra cuenta de Google en https://customer.cloudamqp.com/login:

Creamos la cuenta en CloudAMQP si no tenemos ya la cuenta creada:

Cloud AMQP - 01 - Creación de cuenta
Cloud AMQP – 01 – Creación de cuenta

Creamos una instancia: pulsamos el botón Create New Instance

Cloud AMQP - 02 - Creación de instancia
Cloud AMQP – 02 – Creación de instancia

5.2. Instalamos en la aplicación ADMIN APP de Laravel la conexión a RabbitMQ

Para utilizar RabbitMQ vamos a utilizar el módulo laravel-queue-rabbitmq desarrollado por Vladimir Yuldashev, vamos a empezar instalandolo en el proyecto ADMIN APP para ello accedemos al proyecto cx-lpm-customerdb-admin-app.

La información sobre colas (queues) de Laravel la puedes consultar aquí: Laravel Queues (8.X)

5.2.1. Instalamos el módulo laravel-queue-rabbitmq

$ <strong>docker-compose exec admin-app-lpm composer require vladimir-yuldashev/laravel-queue-rabbitmq</strong>

La instalación de este módulo instamos lo siguiente:

Using version ^11.3 for vladimir-yuldashev/laravel-queue-rabbitmq
 ./composer.json has been updated
 Running composer update vladimir-yuldashev/laravel-queue-rabbitmq
 Loading composer repositories with package information
 Updating dependencies
 Lock file operations: 5 installs, 0 updates, 0 removals
 Locking paragonie/constant_time_encoding (v2.5.0)
 Locking paragonie/random_compat (v9.99.100)
 Locking php-amqplib/php-amqplib (v3.1.2)
 Locking phpseclib/phpseclib (3.0.13)
 Locking vladimir-yuldashev/laravel-queue-rabbitmq (v11.3.0)
 Writing lock file
 Installing dependencies from lock file (including require-dev)
 Package operations: 5 installs, 0 updates, 0 removals
 Downloading paragonie/random_compat (v9.99.100)
 Downloading paragonie/constant_time_encoding (v2.5.0)
 Downloading phpseclib/phpseclib (3.0.13)
 Downloading php-amqplib/php-amqplib (v3.1.2)
 Downloading vladimir-yuldashev/laravel-queue-rabbitmq (v11.3.0)
 Installing paragonie/random_compat (v9.99.100): Extracting archive
 Installing paragonie/constant_time_encoding (v2.5.0): Extracting archive
 Installing phpseclib/phpseclib (3.0.13): Extracting archive
 Installing php-amqplib/php-amqplib (v3.1.2): Extracting archive
 Installing vladimir-yuldashev/laravel-queue-rabbitmq (v11.3.0): Extracting archive
 4 package suggestions were added by new dependencies, use <code>composer suggest</code> to see details.
 Package swiftmailer/swiftmailer is abandoned, you should avoid using it. Use symfony/mailer instead.
 Generating optimized autoload files 
   Illuminate\Foundation\ComposerScripts::postAutoloadDump
   @php artisan package:discover --ansi
   Discovered Package: facade/ignition
   Discovered Package: fruitcake/laravel-cors
   Discovered Package: laravel/sail
   Discovered Package: laravel/sanctum
   Discovered Package: laravel/tinker
   Discovered Package: nesbot/carbon
   Discovered Package: nunomaduro/collision
   Discovered Package: vladimir-yuldashev/laravel-queue-rabbitmq
   Package manifest generated successfully.
   78 packages you are using are looking for funding.
   Use the <code>composer fund</code> command to find out more!
   @php artisan vendor:publish --tag=laravel-assets --ansi --force
   No publishable resources for tag [laravel-assets].
   Publishing complete. 

5.2.2. Añadimos la configuración de la conexión a rabbitmq

En Laravel la configuración de las colas se realiza en el fichero queue.php que se encuentra dentro del proyecto en la carpeta config, si editamos el archivo nos encontramos con algunas definiciones que trae por defecto, añadimos la de RabbitMQ:

 
        'rabbitmq' => [

           'driver' => 'rabbitmq',
           'queue' => env('RABBITMQ_QUEUE', 'default'),
           'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class,

           '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', '/'),
               ],
           ],

           '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),
               ],
               'queue' => [
                   'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class,
               ],
           ],

           /*
            * Set to "horizon" if you wish to use Laravel Horizon.
            */
           'worker' => env('RABBITMQ_WORKER', 'default'),

        ],
    ],

La configuración es sencilla, definimos el driver y el nombre que daremos a la cola, y la clase que utilizamos para la conexión AMQPLazyConnection, a conintuación definimos los parámetros clásicos del host (los valores que se espefican son valores por defecto):

  • host : dirección de donde tenemos alojado nuestro RabbitMQ, en nuestro caso introduciremos la URL que nos indiquen desde …, los datos los introduciremos en el fichero .env (RABBITMQ_HOST)
  • port
  • user
  • password
  • vhost:

Los datos de la conexión los tienes accediendo a la instancia definida anteriormente en CloudAMQP , aquí dentro de DETAILS tenemos todos los datos que necesitamos para la conexión, estos son los míos:

Cloud AMQP - Datos para la conexión con el servidor RabbitMQ
Cloud AMQP – Datos para la conexión con el servidor RabbitMQ

Ahora vamos al fichero .env e introducimos los datos que necesitamos para la conexión:

 
RABBITMQ_HOST=****.****.cloudamqp.com
RABBITMQ_PORT=5672
RABBITMQ_USER=*********
RABBITMQ_PASSWORD=********************************************
RABBITMQ_VHOST=********
RABBITMQ_QUEUE=admin_queue

En mi caso os los muestro ocultos para que algún «amigo» no se dedique a tirarme la conexión, en este fichero avanzamos un concepto que es el nombre de la cola en este caso admin_queue.

Una vez hecho esto es necesario indicar en nuestro proyecto que el sistema de colas que vamos a utilizar es RabbitMQ, esto lo hacemos en el mismo fichero .env en QUEUE_CONNECTION indicando el nombre rabbitmq que hemos definido en el fichero config/queue.php:

 
QUEUE_CONNECTION=rabbitmq

5.3. Creamos los trabajos (jobs) que dispararán el envío de mensajes a RabbitMQ (PRODUCTOR)

Una vez ya tenemos la configuración vamos a definir varios trabajos que creen y transmitan hacía RabbitMQ la publicación de los mensajes necesarios para la creación y modificación de cualquier cliente.

5.3.1. Trabajo (job) para la creación de clientes

En primer lugar creamos un trabajo donde vamos a transmitir el mensaje que se publicará en nuestro broker de mensajería RabbitMQ, para ello creamos un job utilizando artisan:

 
$ docker-compose exec admin-app-lpm php artisan make:job CmCustomerCreated

Con esto en nuestra carpeta de aplicación app se crea la carpeta jobs y ahí tendremos la clase CmCustomerCreated, que definimos de la siguiente forma:

 
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class CmCustomerCreated implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $customer;
    /**
     * Create a new job instance.
     * Creamos una nueva instancia.
     * @return void
     */
    public function __construct($customer)
    {
        $this->customer = $customer;
    }

    /**
     * Execute the job.
     * Ejecución del trabajo.
     * @return void
     */
    public function handle()
    {
        //
    }
}


Basícamente lo que hacemos es definir una variable con los datos que se van a publicar al transmitir el cliente, en nuestro caso serán todos los datos del cliente.

5.3.2. Disparador al crear el cliente

En nuestra API tenemos que definir un disparador con el método dispatch para que se lancé la publicación del mensaje, nos vamos a nuestro controlador App\Http\Controllers\API\CmCustomerController y en el método de creación de cliente (customer) store() añadimos el siguiente código una vez se haya creado el cliente:

 
        $customer = CmCustomer::create($inputCustomer);

        // Launching event on create customer, the array always exists:
        // Lanzamos el evento al crear el cliente, el array siempre existe:
        Log::info('[API/CmCustomerController] Lanzamos el evento al crear el cliente');
        CmCustomerCreated::dispatch($customer->toArray())->onQueue('orders_app_queue'); 

Después de la creación del cliente añadimos el lanzamiento de la publicación del mensaje en este caso la cola en la que publicamos es orders_app_queue que será la que estará escuchando el consumidor en la aplicación ORDERS APP para recibir la información y crear el cliente.

Lo único que nos falta ahora es lanzar el proceso de colas para que en el momento que se cree un cliente lo procese y publique el mensaje en RabbitMQ, para ello ahora lo vamos a hacer de forma manual ejecutando:

 
dafdsfa

5.4. Instalamos en la aplicación ORDER APP la conexión a RabbitMQ

Para la aplicación ORDER APP instalamos el mismo módulo laravel-queue-rabbitmq desarrollado por Vladimir Yuldashev, vamos a empezar instalandolo en el proyecto ORDER APP para ello accedemos al proyecto cx-lpm-customerdb-order-app.

5.4.1. Instalamos el módulo laravel-queue-rabbitmq

$ <strong>docker-compose exec order-app composer require vladimir-yuldashev/laravel-queue-rabbitmq</strong>

5.4.2. Añadimos la configuración de la conexión a rabbitmq

En Laravel la configuración de las colas se realiza en el fichero queue.php que se encuentra dentro del proyecto en la carpeta config, si editamos el archivo nos encontramos con algunas definiciones que trae por defecto, añadimos la de RabbitMQ:

 
        'rabbitmq' => [

           'driver' => 'rabbitmq',
           'queue' => env('RABBITMQ_QUEUE', 'default'),
           'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class,

           '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', '/'),
               ],
           ],

           '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),
               ],
               'queue' => [
                   'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class,
               ],
           ],

           /*
            * Set to "horizon" if you wish to use Laravel Horizon.
            */
           'worker' => env('RABBITMQ_WORKER', 'default'),

        ],

Como ya vimos antes definimos el driver. el nombre que daremos a la cola y la clase que utilizamos para la conexión AMQPLazyConnection, ahora para finalizar la configuración vamos al fichero .env e introducimos los datos que necesitamos para la conexión:

 
RABBITMQ_HOST=****.****.cloudamqp.com
RABBITMQ_PORT=5672
RABBITMQ_USER=*********
RABBITMQ_PASSWORD=********************************************
RABBITMQ_VHOST=********
RABBITMQ_QUEUE=orders_queue

Para la aplicación ORDERS APP definimos una cola diferente que para ADMIN APP en este caso orders_queue, la razón, es que buscamos comunicación bidireccional por lo que necesitamos definir colas diferenciadas para cada dirección.

Ahora en el mismo fichero .env en QUEUE_CONNECTION (por defecto sync) indicamos el nombre de la cola que utilizamos con rabbitmq que es como la hemos definido en el fichero config/queue.php:

 
QUEUE_CONNECTION=rabbitmq

5.4. Definimos en la aplicación ORDER-APP la creación del cliente con el consumo del mensaje de la cola orders_app_queue (CONSUMIDOR)

Con la configuración ya finaliza vamos a preparar nuestra aplicación en primer lugar para recibir los clientes que se creen en ADMIN APP, esta será nuestra primera configuración completa de transmisión con RabbitMQ después a continuación veremos actualización, borrado, …

5.4.1. Trabajo (job) para la creación de clientes

Vamos a definir un trabajo donde estaremos escuchando la publicación de mensajes en la cola correspondiente de hacia ORDERS APP, es decir, en ORDERS APP para la creación de clientes desarrollamos la parte consumidora de RabbitMQ.

Creamos el trabajo como hicimos anteriormente utilizando artisan para crear un trabajo (job), le damos el mismo nombre CmCustomerCreated

 
$ docker-compose exec orders-app php artisan make:job CmCustomerCreated

Con esto en nuestra carpeta de aplicación app se crea la carpeta jobs y ahí tendremos la clase CmCustomerCreated, donde definiremos el método handle(), donde cada vez que llegue un mensaje a la cola se procesará

5.5 Creamos un nuevo trabajo en ADMIN-APP y ORDERS-APP para la actualización y borrado de clientes

Ahora ya hemos visto como crear y manejar la creación de mensajes tanto desde la parte productora como consumidora, así que ahor

5.5.1. Actualización de clientes: trabajo (job) CmCustomerUpdated

A. Creamos el trabajo en ADMIN-APP (PRODUCTOR)

Primera realizamos la parte PRODUCTOR, es decir, creamos el trabajo en ADMIN-APP para la actualización de clientes CmCustomerUpdated:

 
$ docker-compose exec admin-app-lpm php artisan make:job CmCustomerUpdated

En el controlador CmCustomerController añadimos una línea para lanzar la publicación en RabbitMQ de la actualización con:

 
            // Lanzamos el evento al crear el cliente: 
            CmCustomerUpdated::dispatch($customer->toArray())->onQueue('orders_app_queue');

En la parte del trabajo añadimos el mismo código que en CmCustomerCreated:

 
    private $customer;
    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($customer)
    {
        $this->customer = $customer;
    }

Lanzamos el trabajo de las colas con artisan con queue:work (pronto veremos como configurar la ejecución automática):

B. Desarrollamos la recepción del trabajo en ORDERS-APP (CONSUMIDOR)

Ahora que ya envíamos la actualización de nuestro cliente a nuestro broker de mensajería RabbitMQ, pasamos a realizar la parte CONSUMIDORA, en la que vamos a tener en cuenta una posible falta de sincronización y donde se actualice un cliente que todavía no existe en la aplicación ORDERS-APP, veamos como.

En primer lugar creamos el trabajo CmCustomerUpdated:

 
$ docker-compose exec orders-app php artisan make:job CmCustomerUpdated

5.5.2. Eliminación de clientes: trabajo (job) CmCustomerDeleted

A. Creamos el trabajo en ADMIN-APP (PRODUCTOR)

B. Desarrollamos la recepción del trabajo en ORDERS-APP (CONSUMIDOR)

5.X Documentación

En la documentación recogemos algunos documentos y publicaciones que nos han servido de ayuda para la publicación de este tutorial: