This is an old revision of the document!
În cadrul acestui laborator, veți parcurge mai întâi un breviar teoretic pentru a înțelege cum funcționează RabbitMQ, care sunt principalele concepte ale acestei tehnologii, cum să rulați o instanță de RabbitMQ local și cum să o utilizați cu Python. În această primă parte nu este necesar să rulați nimic; materialul oferit are rol de suport pentru partea a doua.
Partea a doua constă într-o serie de exerciții care vor aplica tot ceea ce s-a explicat în breviarul teoretic și vă vor ajuta să aprofundați cunoștințele prin exemple practice.
RabbitMQ este un broker de mesaje open-source bazat pe protocolul AMQP (Advanced Message Queuing Protocol) utilizat pentru a permite aplicațiilor să comunice eficient și asincron prin trimiterea de mesaje între ele. Acesta este folosit pentru a decupla componentele aplicațiilor, facilitând astfel comunicarea, scalabilitatea și reziliența în arhitecturi distribuite și microservicii.
routing key
exact.routing key
.routing key
, permițând un nivel ridicat de flexibilitate. https://www.rabbitmq.com/tutorials/tutorial-five-python
Pentru a conecta o aplicație Python la RabbitMQ, vei folosi biblioteca pika
, care este un client AMQP popular pentru Python. Aceasta permite crearea conexiunilor, configurarea exchange-urilor și queue-urilor, și trimiterea sau recepționarea mesajelor prin RabbitMQ.
Pentru a putea folosi o instanță de RabbitMQ, aveți două posibilități: să o instalați pe calculatorul personal (Installing RabbitMQ | RabbitMQ) sau să rulați următoarea comandă Docker Compose: docker compose up
folosind următorul fișier docker-compose.yamml
:
version: '3.8' services: rabbitmq: image: rabbitmq:3-management container_name: rabbitmq environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest ports: - "5672:5672" # Port for AMQP protocol - "15672:15672" # Port for RabbitMQ management UI volumes: - rabbitmq_data:/var/lib/rabbitmq networks: - rabbitmq_network volumes: rabbitmq_data: networks: rabbitmq_network: driver: bridge
Comandă rulare pentru docker-compose.yaml prezentat anterior:
docker-compose up -d
RabbitMQ oferă și o interfață grafică de management (Management UI) pentru o monitorizare și administrare mai ușoară a mesajelor, queue-urilor și exchange-urilor, fiind accesibilă în mod implicit pe portul 15672 (http://localhost:15672). User-ul și parola implicite sunt guest/guest, însă aceste credențiale funcționează doar pentru accesul de pe localhost; pentru accesul extern este necesară configurarea unor utilizatori noi.
Pentru a conecta o aplicație Python la RabbitMQ, vei folosi biblioteca pika, un client AMQP popular pentru Python. Aceasta permite crearea conexiunilor, configurarea exchange-urilor și queue-urilor, și trimiterea sau recepționarea mesajelor prin RabbitMQ.
Asigură-te că ai instalat biblioteca pika
. Poți face acest lucru rulând următoarea comandă:
pip install pika
Pentru a te conecta la RabbitMQ, va trebui să definești parametrii de conectare. În mod implicit, RabbitMQ rulează local pe portul 5672
, iar contul implicit este guest
cu parola guest
.
import pika # Configurarea conexiunii la RabbitMQ credentials = pika.PlainCredentials("guest", "guest") connection_params = pika.ConnectionParameters(host='localhost', port=5672, credentials=credentials) connection = pika.BlockingConnection(connection_params) channel = connection.channel() print("Conexiunea la RabbitMQ a fost stabilită.")
Acest cod:
localhost
.channel
, care este un canal de comunicare utilizat pentru a interacționa cu RabbitMQ.Notă: Dacă RabbitMQ rulează pe un alt server sau port, schimbălocalhost
cu adresa IP și/sau specifică portul prinConnectionParameters
.
Pentru a trimite și primi mesaje, ai nevoie de un queue (coadă) în care să fie stocate mesajele. Poți crea un queue folosind queue_declare
:
queue_name = 'my_queue' channel.queue_declare(queue=queue_name) print(f"Queue-ul '{queue_name}' a fost creat.")
Acest cod va crea un queue numit my_queue
(sau se va conecta la el dacă deja există). Queue-ul este o structură FIFO (First In, First Out) care stochează mesajele până când sunt preluate de consumatori.
Pentru a trimite un mesaj, vei folosi metoda basic_publish
:
message = "Salut din RabbitMQ!" channel.basic_publish(exchange="", routing_key=queue_name, body=message) print(f"Mesaj trimis: {message}")
exchange=””
specifică că mesajul este trimis direct către queue-ul specificat de routing_key
.routing_key=queue_name
indică queue-ul în care trebuie să ajungă mesajul.body=message
este conținutul mesajului trimis.Pentru a primi mesaje, definești un consumator. Acesta va utiliza un callback pentru a procesa fiecare mesaj primit:
def callback(ch, method, properties, body): print(f"Mesaj primit: {body.decode()}") channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True) print("Aștept mesaje...") channel.start_consuming()
callback
este o funcție care primește și afișează mesajele.basic_consume
configurează consumatorul să asculte queue-ul specificat (queue_name
) și să apeleze callback
pentru fiecare mesaj care este primit (“să consume mesjul”).auto_ack=True
confirmă automat mesajele ca fiind procesate imediat ce sunt primite. După ce sunt procesate, acestea vor fi șterse de pe queue.După ce ai terminat de trimis și primit mesaje, închide conexiunea la RabbitMQ pentru a elibera resursele:
connection.close() print("Conexiunea la RabbitMQ a fost închisă.")
import pika # Conectare la RabbitMQ credentials = pika.PlainCredentials("guest", "guest") connection_params = pika.ConnectionParameters(host='localhost', port=5672, credentials=credentials) connection = pika.BlockingConnection(connection_params) # connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() # Crearea unui queue queue_name = 'my_queue' channel.queue_declare(queue=queue_name) # Trimiterea unui mesaj message = "Salut din RabbitMQ!" channel.basic_publish(exchange='', routing_key=queue_name, body=message) print(f"Mesaj trimis: {message}") # Definirea callback-ului pentru a prelua mesajele def callback(ch, method, properties, body): print(f"Mesaj primit: {body.decode()}") # Configurarea consumatorului pentru a asculta queue-ul specificat channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True) # Începerea consumului de mesaje print("Aștept mesaje...") channel.start_consuming() # Închiderea conexiunii connection.close() print("Conexiunea la RabbitMQ a fost închisă.")
Acest script exemplifică întregul proces de conectare la RabbitMQ, creare de queue, trimitere și recepție de mesaje, și închiderea conexiunii.
În acest exemplu, vom:
fanout
numit broadcast_exchange
.queue1
și queue2
).routing key
(nefiind necesară pentru exchange-ul fanout
).import pika # Conectarea la RabbitMQ connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() # 1. Crearea unui exchange de tip 'fanout' exchange_name = 'broadcast_exchange' channel.exchange_declare(exchange=exchange_name, exchange_type='fanout') print(f"Exchange-ul '{exchange_name}' de tip 'fanout' a fost creat.") # 2. Crearea queue-urilor queue1 = 'queue1' queue2 = 'queue2' channel.queue_declare(queue=queue1) channel.queue_declare(queue=queue2) # 3. Legarea queue-urilor la exchange fără routing key (nu este necesară pentru fanout) channel.queue_bind(exchange=exchange_name, queue=queue1) channel.queue_bind(exchange=exchange_name, queue=queue2) print(f"Queue-urile '{queue1}' și '{queue2}' au fost conectate la exchange-ul '{exchange_name}'.") # 4. Trimiterea unui mesaj către exchange-ul 'fanout' message = "Salut tuturor consumatorilor!" channel.basic_publish(exchange=exchange_name, routing_key='', body=message) print(f"Mesaj trimis către exchange-ul '{exchange_name}': {message}") # Închiderea conexiunii connection.close()
1. Crearea exchange-ului: broadcast_exchange
este un exchange de tip fanout
, care trimite mesajele primite către toate queue-urile conectate la el, indiferent de routing key
.
2. Crearea queue-urilor: Se creează două queue-uri, queue1
și queue2
, fiecare fiind destinat să primească mesajele difuzate de exchange.
3. Legarea queue-urilor la exchange: queue1
și queue2
sunt conectate la exchange-ul broadcast_exchange
fără a specifica routing key
, deoarece aceasta nu este folosită în exchange-urile de tip fanout
.
4. Trimiterea mesajului: Mesajul „Salut tuturor consumatorilor!” este trimis către exchange-ul broadcast_exchange
. Fiind de tip fanout
, exchange-ul va transmite acest mesaj către toate queue-urile conectate (queue1
și queue2
).
- Mesajul „Salut tuturor consumatorilor!” va ajunge atât în queue1
, cât și în queue2
.
Exchange-urile fanout
sunt utile pentru aplicații de tip difuzare (broadcast), unde același mesaj trebuie să fie recepționat de toate queue-urile conectate. Acest tip de exchange este ideal pentru scenarii precum notificările de sistem, unde toate instanțele trebuie să fie informate simultan.
0. Rulați local o instanță de RabbitMQ, fie folosind docker-compose-ul pus la dispoziție mai sus, fie instalând RabbitMQ direct pe mașina voastră. Atenție, dacă optați pentru instalarea directă pe mașina voastră, va fi necesar să instalați separat plugin-ul care oferă acces la interfața web.
1. Creează un script Python care să permită citirea textelor de la tastatură și publicarea acestora într-un exchange nou. Scriptul va citi textul de la utilizator și va publica fiecare mesaj în exchange-ul RabbitMQ, care este creat programatic după realizarea cu succes a conexiunii la RabbitMQ. Exchange-ul va fi configurat din cod imediat după inițializarea conexiunii.
2. Creează un script Python care să creeze un queue, să-l lege la exchange-ul din exercițiul 1 și să afișeze fiecare mesaj primit. Scriptul va crea un queue nou și îl va asocia (binding) cu exchange-ul creat în exercițiul anterior. Pentru fiecare mesaj primit în queue, scriptul va afișa conținutul acestuia pe ecran.
3. Chat interactiv - Pornind de la codul pentru această aplicație de mesagerie, implementează funcționalitățile necesare, marcate cu TODO-uri in fisierul message_service.py, pentru ca aceasta să funcționeze complet:
User: student
Parola: întreabă coordonatorul de laborator
initialize.py
din laborator, care configurează 3 utilizatori: stefan, alex și maria.
nume_prenume_grupa
. Dacă nu ești sigur care este utilizatorul pregătit pentru tine, conectează-te la:
http://acc-atomicsoftware.swedencentral.cloudapp.azure.com:15672
cu utilizatorul si parola de mai sus și caută în tab-ul Queues and Streams după numele tău. Queue-ul o să se numească chatUser.username, iar tu trebuie să folosești doar username pentru a te conecta. În situația în care totuși nu te regăsești, poți folosi unul dintre utilizatorii următori: bob, alice, stefan, alex.
Diagrame explicative ale exercițiului 3: