Creando nuestro primer microservicio

Anteriormente hemos analizado el Uso avanzado de Docker como herramienta para empaquetar nuestras aplicaciones. También estuvimos Usando ficheros Makefile para automatizar nuestras tareas relacionadas con la línea de comandos y además, vimos una Introducción a la programación asíncrona. Veamos cómo podemos aunar todo ello y crear un microservicio resiliente.

Introducción

Los microservicios son un método arquitectónico para crear aplicaciones donde cada función (o servicio) principal se implementa de forma independiente. La arquitectura de microservicios es distribuida y ligeramente acoplada, por lo que un error en un componente no interrumpe toda la aplicación. Los componentes independientes funcionan de manera cooperativa comunicándose mediante API bien definidas.

Los microservicios están muy ligados al concepto de contenedor: una unidad estándar de software que empaqueta el código junto a todas de sus dependencias para que el servicio o aplicación se ejecute de forma rápida y fiable de un entorno informático a otro. Y es aquí donde Docker entra en juego ya que es una plataforma de contenedores cuyo fin es ejecutar aplicaciones de forma aislada y eficiente.

Instalación

$ git clone https://github.com/AlcidesRC/microservice-redis-pubsub.git
$ make composer-install

Configuración

Dentro del docker-compose.yml se encuentra la siguiente sección de variables de entorno:

environment:
    - SUBSCRIBER__SUBSCRIBE_TO_CHANNEL_1=channel:name1
    - SUBSCRIBER__SUBSCRIBE_TO_CHANNEL_2=channel:name2
    - MONOLOG__LEVEL=200
    - MONOLOG__NAME=redis-pubsub
    - MONOLOG__STREAM=php://stdout
    - REDIS__CLUSTER=redis
    - REDIS__DB=10
    - REDIS__HOST=redis
    - REDIS__PORT=6380
    - REDIS__PREFFIX=pubsub_database_
    - REDIS__SCHEME=tcp

Convenciones

  • La aplicación vuelca el log del proceso en la salida estándar.
  • La aplicación generará entradas en el log etiquetadas con redis-pubsub para así simplificar el proceso de depuración y trazabilidad.
  • A modo de ejemplo, haremos uso de dos canales: channel:name1 y channel:name2 para nuestras tareas PUB/SUB.

Uso

Comandos

En el Makefile podremos encontrar los siguientes comandos:

Command Description
build Builds the container(s) service
up Starts the container(s) in daemonized mode
down Stops the container(s) service
logs Prints out the container(s) log(s)
bash Opens a bash connection to containerised service
... ...
publish-demo-message Publish an message

Construcción/Inicio del servicio

$ make up

Comprobar los logs

$ make logs

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ DISPLAYING LOG OUTPUT FROM SERVICES                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Attaching to redis-pubsub, microservice-redis-pubsub_redis_1
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/php-fpm.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/php-fpm.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/subscriber.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/subscriber.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Set uid to user 0 succeeded
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Set uid to user 0 succeeded
redis-pubsub    | 2021-05-19 08:19:16,787 INFO RPC interface 'supervisor' initialized
redis-pubsub    | 2021-05-19 08:19:16,787 INFO RPC interface 'supervisor' initialized
redis-pubsub    | 2021-05-19 08:19:16,787 CRIT Server 'unix_http_server' running without any HTTP authentication checking
redis-pubsub    | 2021-05-19 08:19:16,787 CRIT Server 'unix_http_server' running without any HTTP authentication checking
redis-pubsub    | 2021-05-19 08:19:16,787 INFO supervisord started with pid 1
redis-pubsub    | 2021-05-19 08:19:16,787 INFO supervisord started with pid 1
redis-pubsub    | 2021-05-19 08:19:17,791 INFO spawned: 'php8-fpm' with pid 7
redis-pubsub    | 2021-05-19 08:19:17,791 INFO spawned: 'php8-fpm' with pid 7
redis-pubsub    | 2021-05-19 08:19:17,795 INFO spawned: 'subscriber' with pid 8
redis-pubsub    | 2021-05-19 08:19:17,795 INFO spawned: 'subscriber' with pid 8
redis-pubsub    | 2021-05-19 08:19:17,844 INFO [ redis-pubsub ]: Successfully subscribed to channel {"channel":"channel:name1"} []
redis-pubsub    | 2021-05-19 08:19:17,844 INFO [ redis-pubsub ]: Successfully subscribed to channel {"channel":"channel:name2"} []
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: php8-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: php8-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: subscriber entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: subscriber entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis_1         | 1:C 19 May 2021 08:19:16.171 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1         | 1:C 19 May 2021 08:19:16.171 # Redis version=6.2.3, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1         | 1:C 19 May 2021 08:19:16.171 # Configuration loaded
redis_1         | 1:M 19 May 2021 08:19:16.172 * monotonic clock: POSIX clock_gettime
redis_1         | 1:M 19 May 2021 08:19:16.172 * Running mode=standalone, port=6380.
redis_1         | 1:M 19 May 2021 08:19:16.172 # Server initialized
redis_1         | 1:M 19 May 2021 08:19:16.172 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis_1         | 1:M 19 May 2021 08:19:16.172 * Ready to accept connections

 🔎  Service log were displayed!

Como se puede comprobar en los logs, el servicio redis-pubsub se ha arrancado automáticamente y se ha puesto a escuchar mensajes en los siguientes canales: channel:name1 and channel:name2.

redis-pubsub    | 2021-05-19 08:19:17,844 INFO [ redis-pubsub ]: Successfully subscribed to channel {"channel":"channel:name1"} []
redis-pubsub    | 2021-05-19 08:19:17,844 INFO [ redis-pubsub ]: Successfully subscribed to channel {"channel":"channel:name2"} []

Publicar mensajes

$ make publish-demo-message id=1112233

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ DEMO: PUBLISH A <demo:event> MESSAGE                                                                                │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

2021-05-19 08:21:33,538 INFO [ redis-pubsub ]: Successfully published to channel {"channel":"channel:name1","payload":"O:29:\"PhpRedisPubSub\\Models\\Message\":4:{s:41:\"\u0000PhpRedisPubSub\\Models\\Message\u0000properties\";a:1:{s:2:\"id\";s:7:\"1112233\";}s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000name\";s:10:\"demo:event\";s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000uuid\";s:36:\"8fc58f4c-ba0f-4190-bf5c-b17b6283d64a\";s:40:\"\u0000PhpRedisPubSub\\Models\\Message\u0000timestamp\";i:1621412493;}"} []
2021-05-19 08:21:33,538 INFO [ redis-pubsub ]: Successfully published to channel {"channel":"channel:name2","payload":"O:29:\"PhpRedisPubSub\\Models\\Message\":4:{s:41:\"\u0000PhpRedisPubSub\\Models\\Message\u0000properties\";a:1:{s:2:\"id\";s:7:\"1112233\";}s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000name\";s:10:\"demo:event\";s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000uuid\";s:36:\"8fc58f4c-ba0f-4190-bf5c-b17b6283d64a\";s:40:\"\u0000PhpRedisPubSub\\Models\\Message\u0000timestamp\";i:1621412493;}"} []

 ✅  Task done!

Comprobar los logs

$ make logs

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ DISPLAYING LOG OUTPUT FROM SERVICES                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Attaching to redis-pubsub, microservice-redis-pubsub_redis_1
redis_1         | 1:C 19 May 2021 08:19:16.171 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1         | 1:C 19 May 2021 08:19:16.171 # Redis version=6.2.3, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1         | 1:C 19 May 2021 08:19:16.171 # Configuration loaded
redis_1         | 1:M 19 May 2021 08:19:16.172 * monotonic clock: POSIX clock_gettime
redis_1         | 1:M 19 May 2021 08:19:16.172 * Running mode=standalone, port=6380.
redis_1         | 1:M 19 May 2021 08:19:16.172 # Server initialized
redis_1         | 1:M 19 May 2021 08:19:16.172 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis_1         | 1:M 19 May 2021 08:19:16.172 * Ready to accept connections
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/php-fpm.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/php-fpm.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/subscriber.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/subscriber.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Set uid to user 0 succeeded
redis-pubsub    | 2021-05-19 08:19:16,784 INFO Set uid to user 0 succeeded
redis-pubsub    | 2021-05-19 08:19:16,787 INFO RPC interface 'supervisor' initialized
redis-pubsub    | 2021-05-19 08:19:16,787 INFO RPC interface 'supervisor' initialized
redis-pubsub    | 2021-05-19 08:19:16,787 CRIT Server 'unix_http_server' running without any HTTP authentication checking
redis-pubsub    | 2021-05-19 08:19:16,787 CRIT Server 'unix_http_server' running without any HTTP authentication checking
redis-pubsub    | 2021-05-19 08:19:16,787 INFO supervisord started with pid 1
redis-pubsub    | 2021-05-19 08:19:16,787 INFO supervisord started with pid 1
redis-pubsub    | 2021-05-19 08:19:17,791 INFO spawned: 'php8-fpm' with pid 7
redis-pubsub    | 2021-05-19 08:19:17,791 INFO spawned: 'php8-fpm' with pid 7
redis-pubsub    | 2021-05-19 08:19:17,795 INFO spawned: 'subscriber' with pid 8
redis-pubsub    | 2021-05-19 08:19:17,795 INFO spawned: 'subscriber' with pid 8
redis-pubsub    | 2021-05-19 08:19:17,844 INFO [ redis-pubsub ]: Successfully subscribed to channel {"channel":"channel:name1"} []
redis-pubsub    | 2021-05-19 08:19:17,844 INFO [ redis-pubsub ]: Successfully subscribed to channel {"channel":"channel:name2"} []
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: php8-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: php8-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: subscriber entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis-pubsub    | 2021-05-19 08:19:18,845 INFO success: subscriber entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: DemoEventHandler has been fired! {"id":"1112233"} []
redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: Message successfully received from channel {"channel":"pubsub_database_channel:name1","payload":"O:29:\"PhpRedisPubSub\\Models\\Message\":4:{s:41:\"\u0000PhpRedisPubSub\\Models\\Message\u0000properties\";a:1:{s:2:\"id\";s:7:\"1112233\";}s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000name\";s:10:\"demo:event\";s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000uuid\";s:36:\"8fc58f4c-ba0f-4190-bf5c-b17b6283d64a\";s:40:\"\u0000PhpRedisPubSub\\Models\\Message\u0000timestamp\";i:1621412493;}"} []
redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: DemoEventHandler has been fired! {"id":"1112233"} []
redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: Message successfully received from channel {"channel":"pubsub_database_channel:name2","payload":"O:29:\"PhpRedisPubSub\\Models\\Message\":4:{s:41:\"\u0000PhpRedisPubSub\\Models\\Message\u0000properties\";a:1:{s:2:\"id\";s:7:\"1112233\";}s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000name\";s:10:\"demo:event\";s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000uuid\";s:36:\"8fc58f4c-ba0f-4190-bf5c-b17b6283d64a\";s:40:\"\u0000PhpRedisPubSub\\Models\\Message\u0000timestamp\";i:1621412493;}"} []

 🔎  Service log were displayed!

Como se puede comprobar, el mensaje publicado ha sido recibido por nuestro subscriber:

redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: Message successfully received from channel {"channel":"pubsub_database_channel:name1","payload":"O:29:\"PhpRedisPubSub\\Models\\Message\":4:{s:41:\"\u0000PhpRedisPubSub\\Models\\Message\u0000properties\";a:1:{s:2:\"id\";s:7:\"1112233\";}s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000name\";s:10:\"demo:event\";s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000uuid\";s:36:\"8fc58f4c-ba0f-4190-bf5c-b17b6283d64a\";s:40:\"\u0000PhpRedisPubSub\\Models\\Message\u0000timestamp\";i:1621412493;}"} []
redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: Message successfully received from channel {"channel":"pubsub_database_channel:name2","payload":"O:29:\"PhpRedisPubSub\\Models\\Message\":4:{s:41:\"\u0000PhpRedisPubSub\\Models\\Message\u0000properties\";a:1:{s:2:\"id\";s:7:\"1112233\";}s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000name\";s:10:\"demo:event\";s:35:\"\u0000PhpRedisPubSub\\Models\\Message\u0000uuid\";s:36:\"8fc58f4c-ba0f-4190-bf5c-b17b6283d64a\";s:40:\"\u0000PhpRedisPubSub\\Models\\Message\u0000timestamp\";i:1621412493;}"} []

Además, podemos comprobar que la lógica de negocio asociada a cada mensaje ha sido ejecutada correctamente:

redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: DemoEventHandler has been fired! {"id":"1112233"} []
redis-pubsub    | 2021-05-19 08:21:33,539 INFO [ redis-pubsub ]: DemoEventHandler has been fired! {"id":"1112233"} []

Descarga

Puedes descargar el proyecto de ejemplo desde aquí.

Versión del documento

[^v1.0]: Última Modificación: 19/05/2021