Skip to main content

Cozi de mesaje

În multe cazuri, am putea avea o aplicație unde este necesar să realizăm anumite procesări în mod asincron, separat de fluxul principal al aplicației, cu scopul de a îmbunătăți performanțele și timpii de răspuns ai aplicației noastre. Un mod de a realiza acest lucru este prin intermediul cron jobs și task-uri de background, dar o variantă mai facilă este utilizarea cozilor asincrone de mesaje.

RabbitMQ este un exemplu popular de broker asincron de mesaje care este ușor de pornit și configurat, putând gestiona milioane de mesaje. RabbitMQ funcționează pe modelul publish/subscribe, unde procese de tip publisher generează date care sunt stocate de broker-ul RabbitMQ în cozi, iar procese de tip subscriber se abonează la cozile respective și primesc mesajele atunci când acestea sunt publicate.

În cadrul exemplului din acest laborator, aplicația Testapp, pe care am văzut-o înainte, va avea rolul de publisher. Atunci când se trimite un POST pe ruta generate_event cu un parametru numit event, se va publica evenimentul respectiv într-o coadă numită task_queue. De cealaltă parte, va mai exista o aplicație separată numită Worker (ale cărei surse se găsesc aici) care va acționa ca subscriber. Se va abona la mesajele din coada task_queue și le va afișa atunci când le primește. Puteți găsi și o imagine Docker deja construită pentru Worker pe Docker Hub cu numele mobylab/idp-laborator4-worker.

caution

Este util de menționat faptul că, pentru a realiza conexiunea cu broker-ul RabbitMQ atât în Testapp, cât și în Worker, folosim pachetul Pika, deoarece acesta oferă o implementare a protocolului AMQP 0-9-1 folosit de RabbitMQ.

Având imaginile pentru Testapp și Worker, putem să ne actualizăm fișierul Docker Compose astfel:

  • adăugăm un serviciu de RabbitMQ cu imaginea rabbitmq:management-alpine și îl punem într-o rețea nouă
  • punem serviciul testapp (deja existent) și în această nouă rețea
  • adăugăm serviciul Worker și îl punem în aceeași rețea.

Cu aceste modificări, ajungem la următorul fișier Docker Compose, pe care îl puteți găsi în repository-ul Docker:

prometheus-nexporter-cadvisor-testapp-loki-rmq-stack.yml
version: "3.8"

services:
prometheus:
image: prom/prometheus
volumes:
- ../configs/prometheus/prometheus-nexporter-cadvisor-testapp.yml:/etc/prometheus/prometheus.yml
ports:
- 9090:9090
networks:
- monitoring
- visualizing

node_exporter:
image: prom/node-exporter
deploy:
mode: global
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
ports:
- 9100:9100
networks:
- monitoring

cadvisor:
image: gcr.io/cadvisor/cadvisor
deploy:
mode: global
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk:/dev/disk/:ro
ports:
- 8080:8080
networks:
- monitoring

testapp:
image: mobylab/idp-laborator4-testapp
logging:
driver: loki
options:
loki-url: http://host.docker.internal:3100/loki/api/v1/push
loki-external-labels: job=myjob,owner=radu,environment=development
ports:
- 8000:8000
- 5000:5000
depends_on:
- rabbitmq
networks:
- monitoring
- logging
- rmq

rabbitmq:
image: rabbitmq:management-alpine
ports:
- 5672:5672
- 15672:15672
networks:
- rmq
hostname: rabbitmq

worker:
image: mobylab/idp-laborator4-worker
depends_on:
- rabbitmq
networks:
- rmq

loki:
image: grafana/loki
volumes:
- ../configs/loki/loki.yml:/etc/config/loki.yml
- ../configs/loki/wal:/wal
entrypoint:
- /usr/bin/loki
- -config.file=/etc/config/loki.yml
ports:
- 3100:3100
networks:
- logging
- visualizing

grafana:
image: grafana/grafana
volumes:
- grafana-volume:/var/lib/grafana
ports:
- 3000:3000
depends_on:
- loki
deploy:
placement:
constraints: [node.role == manager]
networks:
- visualizing

networks:
monitoring:
logging:
visualizing:
rmq:

volumes:
grafana-volume:

Odată ce deployment-ul este făcut, putem întâi să verificăm dacă broker-ul RabbitMQ este funcțional intrând pe http://IP:15672/ și logându-ne cu credențialele implicite guest / guest. Putem astfel observa că avem o singură coadă (task_queue) pe care nu s-a publicat încă vreun mesaj.

img

Dacă vrem să publicăm mesaje, este suficient să trimitem o cerere de POST pe ruta generate_event cu un parametru numit event către Testapp (adică pe portul 5000). În consola administrativă a RabbitMQ, putem observa prezența mesajului.

img

De asemenea, putem să ne uităm la log-urile aplicației Worker și să verificăm că mesajul a fost primit cu succes, astfel (presupunând că stiva noastră de servicii se numește prom):

$ docker service logs prom_worker                                                            

prom_worker.1.q3clmboqw5ij@docker-desktop | Worker started
prom_worker.1.q3clmboqw5ij@docker-desktop | Worker connected
prom_worker.1.q3clmboqw5ij@docker-desktop | Received hello

Pentru a vă ușura testarea acestui laborator, găsiți în directorul postman din repository-ul Configs o colecție de rute pe care le puteți importa direct în Postman.