Differences

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

Link to this comparison view

isi:laboratoare:07 [2025/11/15 10:43]
alexandru.predescu [Breviar teoretic]
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>
 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 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 473: Line 473:
 Creează un script care citește mesaje de la tastatură și le publică pe un topic MQTT. Creează un script care citește mesaje de la tastatură și le publică pe un topic MQTT.
  
-<code python> +<note>
-import paho.mqtt.client as mqtt+
  
-client = mqtt.Client() +Date de conectare la broker-ul MQTT în cloud:
-client.connect("​localhost",​ 1883)+
  
-while True: +<code python> 
-   message ​input("Message: ​") +BROKER ​= "isilab.cloud.shiftr.io
-if message ​== "exit": +PORT 1883 
-   break +USERNAME ​= "isilab
-    +PASSWORD = "​oonhdB7qBZrPK2Lf"​ 
-client.publish("​chat/​general"​, message) +TOPIC = "​chat/​general"​
-client.disconnect()+
 </​code>​ </​code>​
 +
 +Atenție! Daca toți colegii se conecteaza pe același topic, mesajele vor ajunge și la ei (și invers)
 +
 +</​note>​
  
 == 2. Subscriber pentru mesaje == == 2. Subscriber pentru mesaje ==
  
 Creează un script care ascultă topic-ul și afișează mesajele primite. Creează un script care ascultă topic-ul și afișează mesajele primite.
- 
-<code python> 
-import paho.mqtt.client as mqtt 
- 
-def on_message(client,​ userdata, msg): 
-   ​print(f"​Primit:​ {msg.payload.decode()}"​) 
- 
-client = mqtt.Client() 
-client.connect("​localhost",​ 1883) 
- 
-client.subscribe("​chat/​general"​) 
-client.on_message = on_message 
- 
-print("​Ascult mesaje pe chat/​general..."​) 
-client.loop_forever() 
-</​code>​ 
  
 == 3. Chat MQTT între 2 clienți == == 3. Chat MQTT între 2 clienți ==
Line 515: 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.1763196238.txt.gz · Last modified: 2025/11/15 10:43 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