This shows you the differences between two versions of the page.
isi:laboratoare:07 [2024/11/17 18:01] stefanel.turcu |
isi:laboratoare:07 [2024/11/21 11:04] (current) sorin.ciolofan |
||
---|---|---|---|
Line 184: | Line 184: | ||
</code> | </code> | ||
- | == Exemplu Complet == | + | == Extra == |
+ | Pentru a vedea un exemplu complet despre cum două aplicații, un ''sender'' și un ''receiver'', comunică folosind RabbitMQ, precum și pentru a citi explicații mai detaliate, vizitați acest tutorial: [[https://www.rabbitmq.com/tutorials/tutorial-one-python|Hello World]]. | ||
- | <code python> | + | În cadrul acestuia veți găsi exemplificat întregul proces de conectare la RabbitMQ: crearea unei cozi (queue), trimiterea și recepția mesajelor, precum și închiderea conexiunii. |
- | 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ă.") | + | |
- | </code> | + | |
- | + | ||
- | Acest script exemplifică întregul proces de conectare la RabbitMQ, creare de queue, trimitere și recepție de mesaje, și închiderea conexiunii. | + | |
=== Exemplu de Utilizare a unui Exchange ''fanout'' în RabbitMQ === | === Exemplu de Utilizare a unui Exchange ''fanout'' în RabbitMQ === | ||
+ | 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”**. | ||
În acest exemplu, vom: | În acest exemplu, vom: | ||
Line 262: | Line 229: | ||
connection.close() | connection.close() | ||
</code> | </code> | ||
- | |||
- | == Explicații Pas cu Pas == | ||
- | |||
- | 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''). | ||
== Rezultatul Așteptat == | == Rezultatul Așteptat == | ||
Line 283: | Line 240: | ||
=== Partea 2 - Exerciții: === | === Partea 2 - Exerciții: === | ||
- | 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.** | + | <note> |
- | 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. | + | 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.** | 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. | + | 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> | ||
+ | channel.queue_bind(exchange=exchange_name, queue=queue_name) | ||
+ | </code> | ||
- | 3. **Chat interactiv** - Pornind de la codul pentru această aplicație de mesagerie, implementează funcționalitățile necesare pentru ca aceasta să funcționeze complet: | + | 3. **Chat interactiv** |
- | <note> | + | Această aplicație presupune un server Flask care interacționează cu RabbitMQ și expune o interfață web pentru utilizator. |
- | Descărcați proiectul de pe GitHub: [[https://github.com/ACS-ISI/Messenger/tree/main|Messenger]] | + | |
- | </note> | + | |
- | * 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. | + | **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: |
- | * 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). | + | |
- | * c) Implementează trimiterea unui mesaj către un anumit utilizator pentru a putea iniția o conversație privată cu acesta. | + | * 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. |
- | * d) Testează funcționalitatea pentru a verifica dacă poți comunica cu un alt utilizator. | + | * 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). |
- | * e) Schimbă conexiunea la RabbitMQ cu următoarea și testează dacă poți conversa cu alți colegi. | + | * 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> | ||
- | **Host: ** acc-atomicsoftware.swedencentral.cloudapp.azure.com | + | |
- | + | Folosește instanța locală și rulează scriptul ''initialize.py'' din laborator, care configurează 3 utilizatori: **stefan**, **alex** și **maria**. | |
- | **User: ** student | + | |
- | + | **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ă. | |
- | **Parola: ** întreabă coordonatorul de laborator | + | |
+ | Pentru a rula aplicația, folosește comanda: ''python app.py'' sau ''flask run''. | ||
+ | |||
+ | Aceasta va rula implicit pe portul 5000, iar pentru a vizualiza interfața pusă la dispoziție de aceasta, deschideți [[http://localhost:5000|]]. | ||
+ | |||
+ | Pentru a rula aplicația pe un alt port decât cel implicit, rulați ''flask run <nowiki>--</nowiki>port 5001''. | ||
</note> | </note> | ||
+ | |||
+ | **Testare:** | ||
+ | |||
+ | * Testează funcționalitatea pentru a verifica dacă poți comunica cu un alt utilizator (folosind două instanțe locale de aplicație). | ||
+ | * Schimbă conexiunea la RabbitMQ cu următoarea și testează dacă poți conversa cu alți colegi: | ||
+ | |||
+ | <code> | ||
+ | Host: acc-atomicsoftware.swedencentral.cloudapp.azure.com | ||
+ | User: student | ||
+ | Parola: întreabă coordonatorul de laborator | ||
+ | </code> | ||
<note important> | <note important> | ||
- | În cadrul acestui exercițiu, nu vei declara queue-uri sau exchange-uri, deoarece acestea sunt deja create. Până să te conectezi la instanța din cloud, folosește instanța locală și rulează scriptul ''initialize.py'' din laborator, care configurează 3 utilizatori: **stefan**, **alex** și **maria**. | + | În cadrul acestui exercițiu, nu vei declara queue-uri sau exchange-uri, deoarece acestea sunt deja create pe instanța din cloud. |
- | </note> | + | |
- | <note tip>**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 tine, conectează-te la: | + | **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 tine, conectează-te la: |
[[http://acc-atomicsoftware.swedencentral.cloudapp.azure.com:15672]] | [[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//.</note> | 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> | ||
+ | </note> | ||
**Diagrame explicative ale exercițiului 3:** | **Diagrame explicative ale exercițiului 3:** | ||
+ | <spoiler> | ||
{{:isi:laboratoare:general-architecture.jpg?400|}} | {{:isi:laboratoare:general-architecture.jpg?400|}} | ||
Line 330: | Line 331: | ||
{{:isi:laboratoare:rabbitmq-send-message.jpg?500|}} | {{:isi:laboratoare:rabbitmq-send-message.jpg?500|}} | ||
+ | </spoiler> | ||