PHP 8.2: ¿Qué hay de nuevo?
La nueva versión de PHP está a la vuelta de la esquina. ¿Cuáles son las nuevas implementaciones que debe conocer? Consulte este artículo para saberlo.

Lee la primera parte de nuestra serie PHP dedicada a la comunicación de microservicios en el framework Symfony y la forma más popular - la comunicación AMQP usando RabbitMQ.
La arquitectura moderna de las aplicaciones ha obligado a los desarrolladores a cambiar la forma de concebir la comunicación entre los distintos componentes de los sistemas informáticos. Antes el asunto era más sencillo: la mayoría de los sistemas se creaban como estructuras monolíticas conectadas entre sí por una red de conexiones de lógica empresarial. Mantener esas dependencias en un PHP proyecto fue un gran reto para Desarrolladores PHPy la creciente popularidad de las soluciones SaaS y el enorme aumento de la popularidad de nube servicios hizo que hoy oigamos hablar cada vez más de microservicios y modularidad de aplicaciones.
¿Cómo, creando microservicios independientes, podemos hacer que intercambien información entre sí?
Este artículo es el primero de una serie sobre comunicación de microservicios en Symfony y cubre la forma más popular - la comunicación AMQP usando RabbitMQ.
Crear dos aplicaciones independientes y lograr la comunicación entre ellas utilizando únicamente el Bus de Mensajes.
Tenemos dos aplicaciones imaginarias e independientes:
* aplicación1
que envía notificaciones por correo electrónico y SMS a los empleados
* aplicación2
que permite gestionar el trabajo de los empleados y asignarles tareas.
Queremos crear un sistema moderno y sencillo mediante el cual la asignación de trabajo a un empleado en aplicación2
enviará una notificación al cliente utilizando aplicación1
. A pesar de las apariencias, ¡esto es muy sencillo!
Para el propósito de este artículo, utilizaremos la última versión de Symfony (versión 6.1 en el momento de escribir este artículo) y la última versión de PHP (8.1). En unos sencillos pasos crearemos un entorno Docker local con dos microservicios. Todo lo que necesitas es:
* un ordenador que funcione,
* instalado Docker + Entorno Docker Compose
* y un Symfony CLI y algo de tiempo libre.
Utilizaremos las capacidades de Docker como herramienta de virtualización y contenedorización de aplicaciones. Empecemos creando un árbol de directorios, un marco para dos Aplicaciones Symfonyy describir la infraestructura de nuestros entornos utilizando la herramienta docker-compose.yml
archivo.
cd ~
mkdir microservicios-en-symfony
cd microservicios-en-symfony
symfony nueva app1
symfony nueva app2
tocar docker-compose.yml
Hemos creado dos directorios para dos aplicaciones Symfony distintas y hemos creado un directorio vacío docker-compose.yml
para lanzar nuestro entorno.
Añadamos las siguientes secciones a docker-compose.yml
file:
versión: '3.8
servicios:
app1:
containername: app1
build: app1/.
restart: on-failure
envfile: app1/.env
entorno:
APPNAME: app1
tty: true
stdinopen: true
app2:
containername: app2
build: app2/.
restart: on-failure
envfile: app2/.env
entorno:
APPNAME: app2
tty: true
stdinopen: true
rabbitmq
containername: rabbitmq
imagen: rabbitmq:gestión
puertos:
- 15672:15672
- 5672:5672
entorno:
- RABBITMQDEFAULTUSER=usuario
- RABBITMQDEFAULT_PASS=contraseña
Fuente código disponible directamente: thecodest-co/microservices-in-symfony/blob/main/docker-compose.yml
Pero espera, ¿qué ha pasado aquí? Para aquellos que no estén familiarizados con Docker, el archivo de configuración anterior puede parecer enigmático, sin embargo su propósito es muy simple. Usando Docker Compose estamos construyendo tres "servicios":
Para que funcione correctamente, seguimos necesitando Dockerfile
que son la fuente para construir las imágenes. Así que vamos a crearlos:
touch app1/Dockerfile
touch app2/Dockerfile
Ambos archivos tienen exactamente la misma estructura y tienen el siguiente aspecto:
DE php:8.1
COPIAR --from=composer:latest /usr/bin/composer /usr/local/bin/composer
COPIAR . /app/
directorio de trabajo /app/
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
EJECUTAR chmod +x /usr/local/bin/install-php-extensions && sync &&
install-php-extensions amqp
EJECUTAR apt-get update
&& apt-get install -y libzip-dev wget --no-install-recommends
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
EJECUTAR docker-php-ext-install zip;
CMD bash -c "cd /app && composer install && php -a"
Código fuente disponible directamente: /thecodest-co/microservices-in-symfony/blob/main/app1/Dockerfile
El archivo anterior es utilizado por Docker Compose para construir un contenedor a partir de una imagen PHP 8.1 con Composer y la extensión AMQP instalada. Además, ejecuta el intepreter PHP en modo append para mantener el contenedor en ejecución en segundo plano.
Su árbol de directorios y archivos debería tener ahora el siguiente aspecto:
.
├── app1
│ └── Dockerfile
| (...) # Estructura de la app Symfony.
├── app2
│ └── Dockerfile
| (...) # Symfony App Structure
└── docker-compose.yml
Empecemos por el aplicación1
y la primera aplicación.
En nuestro ejemplo, se trata de una aplicación que escucha y consume mensajes de la cola enviados por aplicación2
como se describe en los requisitos:
asignar un trabajo a un trabajador en
aplicación2
enviará una notificación al cliente
Empecemos por añadir las librerías necesarias. AMQP es compatible de forma nativa con symfony/messenger
extensión. Además, instalaremos monólogo/monolog
para realizar un seguimiento de los registros del sistema y facilitar el análisis del comportamiento de las aplicaciones.
cd app1/
symfony composer req amqp ampq-messenger monolog
Tras la instalación, se añadió un archivo adicional en config/paquetes/messenger.yaml
. Es un archivo de configuración para el componente Symfony Messenger y no necesitamos su configuración completa.
Sustitúyalo por el siguiente archivo YAML:
marco:
messenger:
# Descomente esto (y el transporte fallido a continuación) para enviar mensajes fallidos a este transporte para su posterior gestión.
# failure_transport: fallido
transportes:
# https://symfony.com/doc/current/messenger.html#transport-configuration
mensajes_externos:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
opciones:
auto_setup: false
intercambio:
nombre: mensajes
type: direct
default_publish_routing_key: from_external
colas:
messages:
binding_keys: [from_external]
Código fuente disponible directamente: thecodest-co/microservices-in-symfony/blob/main/app1/config/packages/messenger.yaml
Symfony Messenger se utiliza para la comunicación síncrona y asíncrona en aplicaciones Symfony. Soporta una variedad de transportao fuentes de verdad de la capa de transporte. En nuestro ejemplo, utilizamos la extensión AMQP que soporta el sistema de colas de eventos RabbitMQ.
La configuración anterior define un nuevo transporte denominado mensajes_externos
que hace referencia al MESSENGER_TRANSPORT_DSN
y define la escucha directa en mensajes
en el Bus de Mensajes. En este punto, edite también el app1/.env
y añada la dirección de transporte correspondiente.
"`env
(...)
MESSENGERTRANSPORTDSN=amqp://usuario:contraseña@rabbitmq:5672//messages
(...)
"
Después de preparar el framework de la aplicación y configurar las librerías, es hora de implementar la lógica de negocio. Sabemos que nuestra aplicación debe responder a la asignación de un trabajo a un trabajador. También sabemos que la asignación de un trabajo en theapp2system cambia el estado del trabajo. Así que vamos a crear un modelo que imita el cambio de estado y guardarlo en la rutaapp1/Message/StatusUpdate.php`:
{
public function __construct(cadena protegida $status){}
public function getStatus(): cadena
{
return $this->estado;
}
}
Código fuente disponible directamente: /thecodest-co/microservices-in-symfony/blob/main/app1/src/Message/StatusUpdate.php
Todavía necesitamos una clase que implemente la lógica de negocio cuando nuestro microservicio reciba el evento anterior de la cola. Así que vamos a crear una clase Gestor de mensajes en el app1/Handler/StatusUpdateHandler.php
camino:
use PsrLogLoggerInterface;
use SymfonyComponentMessengerAttributeAsMessageHandler;
[AsMessageHandler]
class StatusUpdateHandler
{
función pública __construct(
protected LoggerInterface $logger,
) {}
public function __invoke(StatusUpdate $statusUpdate): void
{
$statusDescription = $statusUpdate->getStatus();
$this->logger->warning('APP1: {STATUS_UPDATE} - '.$statusDescription);
// el resto de la lógica de negocio, es decir, el envío de correo electrónico al usuario
// $this->emailService->email()
}
}
Código fuente disponible directamente: /thecodest-co/microservices-in-symfony/blob/main/app1/src/Handler/StatusUpdateHandler.php
PHP facilitan mucho las cosas y hacen que, en este caso concreto, no tengamos que preocuparnos del autocableado ni de la declaración de servicios. Nuestro microservicio para gestionar eventos de dominio está listo, es hora de pasar a la segunda aplicación.
Echaremos un vistazo a la aplicación2
y el segundo Aplicación Symfony. Nuestra idea es enviar un mensaje a la cola cuando a un trabajador se le asigna una tarea en el sistema. Así que vamos a hacer una configuración rápida de AMQP y conseguir que nuestro segundo microservicio empiece a publicar StatusUpdate
al bus de mensajes.
La instalación de las bibliotecas es exactamente igual que para la primera aplicación.
cd ..
cd app2/
symfony composer req amqp ampq-messenger monolog
Asegurémonos de que el app2/.env
contiene una entrada DSN válida para RabbitMQ:
(...)
MESSENGER_TRANSPORT_DSN=amqp://usuario:contraseña@rabbitmq:5672//mensajes
(...)
Todo lo que queda es configurar Symfony Messenger en el archivo app2/config/paquetes/messenger.yaml
file:
marco:
messenger:
# Descomente esto (y el transporte fallido a continuación) para enviar mensajes fallidos a este transporte para su posterior gestión.
# failure_transport: fallido
transportes:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
enrutamiento:
# Enruta tus mensajes a los transportes
AppMessageStatusUpdate': async
Como puede ver, esta vez la definición de transporte apunta directamente a async
y define el enrutamiento en forma de envío de nuestro StatusUpdate
al DSN configurado. Esta es la única área de configuración, sólo queda crear la capa lógica y de implementación de la cola AMQP. Para ello crearemos el gemelo StatusUpdateHandler
y StatusUpdate
clases en aplicación2
.
use PsrLogLoggerInterface;
use SymfonyComponentMessengerAttributeAsMessageHandler;
[AsMessageHandler]
class StatusUpdateHandler
{
función pública __construct(
private readonly LoggerInterface $logger,
) {}
public function __invoke(StatusUpdate $statusUpdate): void
{
$statusDescription = $statusUpdate->getStatus();
$this->logger->warning('APP2: {STATUS_UPDATE} - '.$statusDescription);
## lógica de negocio, es decir, el envío de notificación interna o la cola de algunos otros sistemas
}
}
{
public function __construct(cadena protegida $status){}
public function getStatus(): cadena
{
return $this->estado;
}
}
Código fuente disponible directamente: /thecodest-co/microservices-in-symfony/blob/main/app2/src/Message/StatusUpdate.php
Finalmente, todo lo que hay que hacer es crear una forma de enviar un mensaje al Bus de Mensajes. Crearemos un simple Comando Symfony por esto:
use SymfonyComponentConsoleAttributeAsCommand;
use SymfonyComponentConsoleCommandComando;
use SymfonyComponentConsoleInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use SymfonyComponentMessengerMessageBusInterface;
[AsCommand(
nombre: "app:enviar"
)]
class SendStatusCommand extends Comando
{
public function construct(private readonly MessageBusInterface $messageBus, string $name = null)
{
parent::construct($name);
}
protected function ejecutar(InputInterface $input, OutputInterface $output): int
{
$status = "Trabajador X asignado a Y";
$this->messageBus->dispatch(
mensaje: new StatusUpdate($status)
);
return Command::SUCCESS;
}
}
Gracias a Inyección de dependencia podemos utilizar una instancia de Interfaz MessageBus
en nuestro Comando y enviar un StatusUpdate
mensaje a través del enviar()
a nuestra cola. Además, también utilizamos PHP Atributos aquí.
Eso es todo - todo lo que queda es ejecutar nuestro entorno Docker Compose y ver cómo se comportan nuestras aplicaciones.
Con Docker Compose los contenedores con nuestras dos aplicaciones se construirán y ejecutarán como instancias separadas, la única capa de middleware será la aplicación rabbitmq
y nuestra implementación del bus de mensajes.
Desde el directorio raíz del proyecto, vamos a ejecutar los siguientes comandos:
cd ../ # asegúrate de que estás en el directorio principal.
docker-compose up --build -d
Este comando puede llevar algún tiempo, ya que construye dos contenedores separados con PHP 8.1 + AMQP y extrae la imagen de RabbitMQ. Tenga paciencia. Después de que las imágenes estén construidas, puedes ejecutar nuestro comando desde aplicación2
y enviar algunos mensajes a una cola.
docker exec -it app2 php bin/console app:send
Puedes hacerlo tantas veces como puedas. Mientras no haya consumidor sus mensajes no serán procesados. En cuanto encienda el aplicación1
y consumir todos los mensajes que mostrarán en tu pantalla.
docker exec -it app1 php bin/console messenger:consume -vv external_messages
El completo código fuente junto con el README se puede encontrar en nuestro repositorio público The Codest Github
Symfony, con sus bibliotecas y herramientas, permite un enfoque rápido y eficaz para el desarrollo de aplicaciones modernas. aplicaciones web. Con unos pocos comandos y unas pocas líneas de código somos capaces de crear un moderno sistema de comunicación entre aplicaciones. Symfony, al igual que PHPes ideal para desarrollo de aplicaciones web y gracias a su ecosistema y a su facilidad de implantación, este ecosistema logra algunos de los mejores indicadores de tiempo de comercialización.
Sin embargo, rápido no siempre significa bueno - en el ejemplo anterior presentamos la forma más simple y rápida de comunicación. Lo que los más curiosos seguramente notarán es que hay una falta de desconexión de eventos de dominio fuera de la capa de aplicación - en la versión actual están duplicados, y no hay soporte completo para Sobre
entre otros no hay Sellos
. Para esos y otros, te invito a leer la Parte II, donde cubriremos el tema de la unificación de la estructura de dominios de las aplicaciones Symfony en un entorno de microservicios, y discutiremos el segundo método popular de comunicación de microservicios - esta vez síncrono, basado en la API REST.