Differences

This shows you the differences between two versions of the page.

Link to this comparison view

isi:laboratoare:07 [2025/11/15 10:48]
alexandru.predescu
isi:laboratoare:07 [2025/11/21 12:52] (current)
stefanel.turcu
Line 117: Line 117:
 == Instalare RabbitMQ == == Instalare RabbitMQ ==
  
-Pentru a putea folosi o instanță de RabbitMQ, aveți două posibilități:​ să o instalați pe calculatorul personal ([[https://​www.rabbitmq.com/​docs/​download|Installing RabbitMQ | RabbitMQ]]) sau să rulați următoarea comandă Docker Compose: ''​docker compose up''​ folosind următorul fișier ''​docker-compose.yamml'':​+Pentru a putea folosi o instanță de RabbitMQ, aveți două posibilități:​ să o instalați pe calculatorul personal ([[https://​www.rabbitmq.com/​docs/​download|Installing RabbitMQ | RabbitMQ]]) sau să rulați următoarea comandă Docker Compose: ''​docker compose up''​ folosind următorul fișier ''​docker-compose.yaml'':​
  
 <code yaml> <code yaml>
Line 167: Line 167:
 Pași pentru conectarea la RabbitMQ folosind Python Pași pentru conectarea la RabbitMQ folosind Python
  
-**1. Instalarea ​Bibliotecii ​pika**+**1. Instalarea ​bibliotecii ​pika**
  
 Asigură-te că ai instalat biblioteca ''​pika''​. Poți face acest lucru rulând următoarea comandă: Asigură-te că ai instalat biblioteca ''​pika''​. Poți face acest lucru rulând următoarea comandă:
Line 258: Line 258:
  
 == Exemplu de Utilizare a unui Exchange ''​fanout''​ în RabbitMQ == == Exemplu de Utilizare a unui Exchange ''​fanout''​ în RabbitMQ ==
 +
 +<spoiler click here>
 +
 Până acum am văzut cum putem crea o coadă (queue), să trimitem un mesaj pe aceasta și să consumăm acel mesaj. În cadrul exemplului anterior, am presupus că un mesaj este trimis către o singură coadă. În ceea ce urmează, vom face ceva complet diferit – vom livra un mesaj către mai mulți consumatori (îl vom publica pe mai multe queue-uri). Acest pattern este cunoscut sub denumirea de **„publish/​subscribe”**. Până acum am văzut cum putem crea o coadă (queue), să trimitem un mesaj pe aceasta și să consumăm acel mesaj. În cadrul exemplului anterior, am presupus că un mesaj este trimis către o singură coadă. În ceea ce urmează, vom face ceva complet diferit – vom livra un mesaj către mai mulți consumatori (îl vom publica pe mai multe queue-uri). Acest pattern este cunoscut sub denumirea de **„publish/​subscribe”**.
  
Line 265: Line 268:
   - Conecta ambele queue-uri la exchange fără a folosi ''​routing key''​ (nefiind necesară pentru exchange-ul ''​fanout''​).   - Conecta ambele queue-uri la exchange fără a folosi ''​routing key''​ (nefiind necesară pentru exchange-ul ''​fanout''​).
   - Trimite un mesaj către exchange, care va fi livrat tuturor queue-urilor conectate.   - Trimite un mesaj către exchange, care va fi livrat tuturor queue-urilor conectate.
 +
 +
 +**Cod publisher**
  
 <code python> <code python>
Line 273: Line 279:
 channel = connection.channel() channel = connection.channel()
  
-1. Crearea unui exchange de tip '​fanout'​+# Crearea unui exchange de tip '​fanout'​
 exchange_name = '​broadcast_exchange'​ exchange_name = '​broadcast_exchange'​
 channel.exchange_declare(exchange=exchange_name,​ exchange_type='​fanout'​) channel.exchange_declare(exchange=exchange_name,​ exchange_type='​fanout'​)
 print(f"​Exchange-ul '​{exchange_name}'​ de tip '​fanout'​ a fost creat."​) print(f"​Exchange-ul '​{exchange_name}'​ de tip '​fanout'​ a fost creat."​)
  
-2. Crearea queue-urilor +# Trimiterea unui mesaj către exchange-ul '​fanout'​
-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!"​ message = "Salut tuturor consumatorilor!"​
 channel.basic_publish(exchange=exchange_name,​ routing_key='',​ body=message) channel.basic_publish(exchange=exchange_name,​ routing_key='',​ body=message)
Line 296: Line 291:
 # Închiderea conexiunii # Închiderea conexiunii
 connection.close() connection.close()
 +</​code>​
 +
 +**Cod subscriber**
 +
 +<code python>
 +import pika
 +
 +# Conectarea la RabbitMQ
 +connection = pika.BlockingConnection(pika.ConnectionParameters('​localhost'​))
 +channel = connection.channel()
 +
 +# 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."​)
 +
 +queue_name = "​queue1"​ # nume diferite pentru fiecare consumator
 +channel.queue_declare(queue=queue_name)
 +
 +# Legare queue la exchange
 +channel.queue_bind(exchange=exchange_name,​ queue=queue_name)
 +
 +print(f"​Ascult mesaje pe '​{queue_name}'​..."​)
 +
 +def callback(ch,​ method, properties, body):
 +    print(f"​[Primit] {body.decode()}"​)
 +
 +channel.basic_consume(queue=queue_name,​ on_message_callback=callback,​ auto_ack=True)
 +
 +# Închiderea conexiunii
 +try:
 +    channel.start_consuming()
 +except KeyboardInterrupt:​
 +    print("​Receiver oprit."​)
 +    connection.close()
 +
 </​code>​ </​code>​
  
Line 301: Line 333:
  
 - Mesajul „Salut tuturor consumatorilor!” va ajunge atât în ''​queue1'',​ cât și în ''​queue2''​. - Mesajul „Salut tuturor consumatorilor!” va ajunge atât în ''​queue1'',​ cât și în ''​queue2''​.
 +
 +</​spoiler>​
 +
  
 <note tip> <note tip>
Line 313: Line 348:
 === Setup MQTT === === Setup MQTT ===
  
-Instalare în Python ​(biblioteca ''​paho-mqtt''​):+Pentru a conecta o aplicație ​Python ​la MQTT, vei folosi ​biblioteca ''​paho-mqtt''​. Aceasta permite crearea și configurarea conexiunilor,​ și trimiterea sau recepționarea mesajelor prin MQTT. 
 + 
 +== 1. Instalarea bibliotecii ''​paho-mqtt''​ == 
 + 
 +Asigură-te că ai instalat biblioteca ''​paho-mqtt''​. Poți face acest lucru rulând următoarea comandă:
  
 <​code>​ <​code>​
Line 320: Line 359:
  
 == Publisher == == Publisher ==
 +
 +Următoarea secvență de cod implementează un nod de tip publisher care trimite mesaje prin MQTT:
  
 <code python> <code python>
Line 345: Line 386:
  
 == Subscriber == == Subscriber ==
 +
 +Următoarea secvență de cod implementează un nod de tip subscriber care primește mesaje venite prin MQTT:
  
 <code python> <code python>
 import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
    
-BROKER = "isilab.cloud.shiftr.io"+BROKER = "localhost"
 PORT = 1883 PORT = 1883
-USERNAME = "isilab+USERNAME = "user
-PASSWORD = "oonhdB7qBZrPK2Lf"+PASSWORD = "pass"
 TOPIC = "​chat/​general"​ TOPIC = "​chat/​general"​
  
Line 369: Line 412:
 </​code>​ </​code>​
  
 +== Interfață grafică ==
  
 +Putem vizualiza datele la nivelul broker-ului de mesaje folosind un GUI precum MQTTBox (windows: [[https://​apps.microsoft.com/​detail/​9nblggh55jzg|from Microsoft Store]], web: [[https://​github.com/​workswithweb/​MQTTBox|from GitHub]])
  
-==== Exerciții ====+{{:​isi:​laboratoare:​mqtt_box_1.png?​600|}}
  
-=== RabbitMQ ===+{{:​isi:​laboratoare:​mqtt_box_2.png?​600|}}
  
-<​note>​ 
-Descărcați proiectul de pe GitHub: [[https://​github.com/​ACS-ISI/​Messenger/​tree/​main|Messenger]] 
-</​note>​ 
  
  
-<note tip> 
-Pentru a instala toate dependențele necesare pentru laboratorul de astăzi, rulați: ''​pip install -r requirements.txt''​. Fișierul ''​requirements.txt''​ poate fi descărcat de aici: [[https://​github.com/​ACS-ISI/​Messenger/​blob/​main/​requirements.txt|requirements.txt]]. 
-</​note>​ 
-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 - [[https://​www.rabbitmq.com/​docs/​management|Plugin]]. 
  
-1. **Creează un script Python care să permită citirea textelor de la tastatură și publicarea acestora într-un exchange nou.** (care este creat programatic după realizarea cu succes a conexiunii la RabbitMQ). Scriptul va citi textul de la utilizator și va publica fiecare mesaj în exchange-ul RabbitMQ (in mod ''​fanout''​). 
  
-<code python> 
-try: 
-   while True: 
-      message = input("​Message:​ ") 
-      channel.basic_publish(exchange=exchange_name,​ routing_key='',​ body=message) 
-finally: 
-    connection.close() 
-</​code>​ 
  
-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.** +==== Exerciții ====
-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 în consolă.+
  
-<code python>​ +==RabbitMQ ​===
-channel.queue_bind(exchange=exchange_name,​ queue=queue_name) +
-</​code>​ +
- +
-3. **Chat interactiv** +
- +
-Această aplicație presupune un server Flask care interacționează cu RabbitMQ ​și expune o interfață web pentru utilizator. +
- +
-**Implementare:​** 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: +
- +
-  * TODO a) Trimite la fiecare 30 de secunde un mesaj de tip broadcast pe exchange-ul '​chat_online_user_exchange'​ și fără nici un routing_key,​ prin care să informezi ceilalți participanți la chat că ești online și disponibil pentru conversație. Asigură-te că acest mesaj are un timp de viață de maximum 2 minute în queue. +
-  * TODO b) Consumă mesajele care sosesc pe queue-ul tău. Adaugă un `consumer_tag` cu numele queue-ului, astfel încât consumatorul să nu mai asculte la acest queue atunci când utilizatorul se deconectează (logout). +
-  * TODO c) Implementează trimiterea unui mesaj către un anumit utilizator pentru a putea iniția o conversație privată cu acesta.+
  
 <note tip> <note tip>
 +Pentru a instala toate dependențele necesare pentru laboratorul de astăzi, rulați: ''​pip install -r requirements.txt''​ folosind fișierul requirements.txt cu următorul conținut:
  
-Folosește instanța locală și rulează scriptul ''​initialize.py''​ din laborator, care configurează ​utilizatori:​ **stefan**, **alex** și **maria**.+<​code>​ 
 +Flask==3.1.0 
 +Flask_SocketIO==5.4.1 
 +pika==1.3.
 +</​code>​
  
-**Testare**:​ Pentru a testa aplicația, puteți rula două instanțe ale aplicației pe porturi diferite, pentru a simula doi utilizatori care folosesc aplicația pe dispozitive diferite. În cadrul testării, punctul a) poate fi verificat individual, iar punctele b) și c) vor fi testate împreună.+</​note>​
  
-Pentru a rula aplicația, folosește comanda: ''​python app.py''​ sau ''​flask run''​.+== 1Setup ==
  
-Aceasta va rula implicit pe portul 5000, iar pentru a vizualiza interfața pusă la dispoziție ​de aceastadeschideți [[http://localhost:​5000|]].+Rulați local o instanță de RabbitMQ, fie folosind //​docker-compose//​-ul pus la dispoziție ​mai susfie 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 - [[https://www.rabbitmq.com/​docs/​management|Plugin]].
  
-Pentru a rula aplicația pe un alt port decât cel implicit, rulați ''​flask run <​nowiki>​--</​nowiki>​port 5001''​. 
-</​note>​ 
  
-**Testare:​**+== 2. Publisher de mesaje ==
  
-  * Testează funcționalitatea pentru a verifica dacă poțcomunica cu un alt utilizator ​(folosind două instanțe locale de aplicație). +Creează un script Python care să permită citirea textelor de la tastatură șpublicarea acestora într-un exchange nou. (care este creat programatic după realizarea cu succes a conexiunii la RabbitMQ). Scriptul va citi textul de la utilizator ​și va publica fiecare mesaj în exchange-ul RabbitMQ (in mod ''​fanout''​).
-  * Schimbă conexiunea ​la RabbitMQ cu următoarea ​și testează dacă poți conversa cu alți colegi:+
  
-<​code>​ +<​code ​python
-Hostacc-atomicsoftware.swedencentral.cloudapp.azure.com +try
-Userstudent +   while True
-Parolaîntreabă coordonatorul de laborator+      ​message = input("​Message") 
 +      channel.basic_publish(exchange=exchange_name,​ routing_key='',​ body=message) 
 +finally: 
 +    connection.close()
 </​code>​ </​code>​
  
-<note important>​ +== 3Subscriber pentru mesaje ==
-În cadrul acestui exercițiu, nu vei declara queue-uri sau exchange-uri,​ deoarece acestea sunt deja create pe instanța din cloud.+
  
-**Important**:​ Când te conectezi la instanța din cloud, utilizatorii vor avea formatul ''​nume_prenume_grupa''​. Dacă nu ești sigur care este utilizatorul pregătit pentru tineconectează-te la:+Creează un script Python ​care să creeze un queuesă-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 în consolă.
  
-[[http://​acc-atomicsoftware.swedencentral.cloudapp.azure.com:​15672]] +<code python
- +channel.queue_bind(exchange=exchange_name,​ queue=queue_name)
-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//.</note+
- +
-<note important>​ +
-În situația în care întâmpinați o eroare similară cu "​connection lost" sau "​unable to pop from an empty queue" atunci când începeți să consumați mesaje, este indicat să adăugați o metodă nouă pentru a crea un channel ​și să utilizați un channel nou creat atunci când începeți să ascultați. +
- +
-<​code>​ +
-  def getChannel(self): +
-    connection ​pika.BlockingConnection(self.connection_params) +
-    return connection.channel()+
 </​code>​ </​code>​
-</​note>​ 
  
- 
-**Diagrame explicative ale exercițiului 3:** 
- 
-<​spoiler>​ 
-{{:​isi:​laboratoare:​general-architecture.jpg?​400|}} 
- 
-{{:​isi:​laboratoare:​rabbitmq-user-alive-notification.jpg?​500|}} 
- 
-{{:​isi:​laboratoare:​rabbitmq-send-message.jpg?​500|}} 
-</​spoiler>​ 
  
 === MQTT === === MQTT ===
Line 484: Line 484:
 TOPIC = "​chat/​general"​ TOPIC = "​chat/​general"​
 </​code>​ </​code>​
 +
 +Atenție! Daca toți colegii se conecteaza pe același topic, mesajele vor ajunge și la ei (și invers)
  
 </​note>​ </​note>​
Line 498: Line 500:
  
 Test: Test:
-  * Trimite mesaje între instanțe +  * Trimite mesaje între instanțe ​(publisher/​subscriber) 
-  * Observă că brokerul rutează mesajele automat+  * Observă că brokerul rutează mesajele automat ​pe baza topic-ului, indiferent de câte instanțe de publisher sau subscriber sunt conectate la broker
  
  
isi/laboratoare/07.1763196529.txt.gz · Last modified: 2025/11/15 10:48 by alexandru.predescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0