Kolmas praktikum - RabbitMQ & kaugprotseduurid
Selles praktikumis vaatame kuidas luua lihtne hajussüsteem, mis võimaldab RPC kaudu välja kutsuda funktsioone teistes süsteemi sõlmedes. Me loome RPC lahenduse ise, ilma RPC teekide või vahevarade kasutamisetta, aga kasutame sõnumite edastamiseks klientide ja serverite vahel RabbitMQ maaklerit.
NB! Jätkame eelmises praktikumis kasutatud RabbitMQ keskkonna kasutamist.
Viited
- RabbitMQ Python teegi, Pika dokumentatsioon: https://pika.readthedocs.io/en/latest/intro.html
- Pika näited: https://pika.readthedocs.io/en/latest/examples.html
Probleemide korral kontrollige:
- Võimalikud probleemid ja nende potentsiaalsed lahendused osa praktikumi juhendi lõpus.
- Küsige otse
#praktikum-3-rpc
Zulip teemas.
Ülesanne 3.1: Uue RabbitMQ virtuaalse keskkonna seadistamine
Kasutame selles praktikumis sama RabbitMQ serverit mida kasutasime eelmine kord, aga loome uue RabbitMQ virtuaalse keskkonna. See aitab meil hoida eelmise hajusrakenduse järjekorrad ning Exhange'id eraldi selle praktikumis loodavatest.
Samuti loome uue kasutaja hspraks03
, kellele anname õigused uut Virtuaalkeskkonda kasutada aga kellel ei ole õigusi RabbitMQ andministreerimisliidest kasutada ning kes ei saa ligi eelmise praktikumi keskkonnale ning andmetele. RabbitMQ'd kasutatavate rakenduste loomisel tuleks vältida administratsiooniõigustega kasutajate kasutamist (nagu me seda tegime eelmises praktikumis).
- Logi sisse RabbitMQ administreerimise liidesesse: http://172.17.66.250:15600/ asendades aadressis asuva pordi (15600) väärtuse enda RabbitMQ pordiga, mille saite eelmise praktikumi alguses.
- Täpse pordi väärtuse, kasutajanime ja parooli sai õppejõu käest eelmises kraktikumis. Igal tudengil on erinev keskkond.
- See server on kätte saadav ainult Ülikooli võrgust.
- Kontrollige, et kasutate Delta majas Eduroam võrku, või VPN'i.
- UT public võrk ei ole piisav.
- Eduroam võrk väljaspoolt Delta maja ei pruugi olla piisav.
- Ülikooli VPN õpetus: https://wiki.ut.ee/pages/viewpage.action?pageId=17105590
- Kontrollige, et kasutate Delta majas Eduroam võrku, või VPN'i.
Loome täiesti uue RabbitMQ virtuaalse keskkonna ja kasutaja selle praktikumi jaoks.
- Loo uus RabbitMQ Virtuaalne keskkond (Virtual Enviorenment)
- Ava RabbitMQ
Admin
lehekülg (üleval keskel on link) - Vali Virtual
Hosts
(paremal ülevalpool on link) - Lisa uus Virtuaalne keskkond (Virtual Host)
- Pane nimeks
hspraks03
- Pane nimeks
- Ava RabbitMQ
- Loo uus RabbitMQ kasutaja
- Ava RabbitMQ
Admin
lehekülg (üleval keskel on link) - Username:
hspraks03kasutaja
- Parool vali ise vabalt, aga jäta see meelde kuna meil on seda hiljem vaja Python programmi jaoks seadistada
- Ava RabbitMQ
- Seadistame uuele kasutajale õigused kasutada ainult
hspraks03
virtuaalkeskkonda- Klikki kasutaja nimel kasutajate administreerimisvaates
- Vali
Permissions
- Vali VirtualHost
hspraks03
, jäta muud valikud samaks ja klikki nupulSet permission
- Edaspidi kasutame selle praktikumis loodud rakendustes
hspraks03kasutaja
kasutajat.
Ülesanne 3.2: RPC Kliendi implementeerimine
Loome Python RabbitMQ põhise RPC Kliendi.
NB! Selles praktikumis aktiveerime ka Python logimise. Lisaks sellele, et see aitab meie programmidel paremini logida infot ja veateateid, annab see meile võimaluse ka RabbitMQ spetsiifilist logide sisu näha.
- Tõmmake alla Pythoni rpc kliendi skeleton: rpcklient.py
- PS! "..." koodis tuleb asendada teie poolt kirjutatud koodiga.
- NB! asendage koodis RabbitMQ serveri IP ära!
- Implementeerime puuduvad osad:
if __name__ == _main_:
meetod hoolitseb programmi käivitamise eestbroker_port
väärtus peaks olema see port, mida te saite eelmise praktikumi käigus (56YZ).routing_key
väärtus peaks olema funktsiooni nimi, mida me soovime välja kutsuda. Hiljem, serveri programmis peame kasutama sama nime järjekorra nimena. Jätke see meelde.-
RabbitMQRpcClient()
klassi objekt valmistab ette RPC objekti mille kaudu saame funktsiooni välja kutsuda. sisend
on argument meie funktsioonile. Kuna andmeid peab saatma hiljem RabbitMQ sõnumina, siis loome Python sõnaraamatu (dictionary), mis sisaldab kõiki funktsiooni argumente. Siin on argumendid:- raamat: Gutenberg raamatu id (nagu esimeses praktikumis)
- sone: Sõne, mida soovime raamatust otsida.
json.dumps()
funktsiooni abil konverteeritakse see objekt string väärtuseks ning hiljem saab seda sama moodi tagasi JSON tüüpi objektiks konverteerida.
class RabbitMQRpcClient(object)
klassis:ef __init__(self, broker_host, broker_port, credentials, routing_key):
meetod- Seadistame ühenduse loomise:
self.connection = pika.BlockingConnection( pika.ConnectionParameters(host=broker_host, port=broker_port, virtual_host= "hspraks03", credentials=credentials))
- virtual_host on siin varasemalt loodud virtuaalse keskkonna nimi
- Ülejäänud väärtused on samad eelmise praktikumi koodile.
- Seadistame kanali loomise:
self.channel = self.connection.channel()
- Seadistame järjekorra loomise:
result = self.channel.queue_declare(queue='', exclusive=True)
- Siin on järjekorra nimeks tühi sõne, mis tähendab seda, et RabbitMQ seadistab meie kliendile unikaalse genereeritud järjekorra.
- exclusive=True tulemusena teised kliendid seda järjekorda kuulata ei saa.
- Sellesse järjekorda saabuvad hiljem serveri vastused meie kliendile.
- automaatselt genereeritud järjekorra nime saame kätte läbi result objekti nii:
result.method.queue
- Selle peaks seadma klassi muutuja
self.callback_queue
väärtuseks.
- Alustame vastuste kuulamist, kuhu peaksid hiljem jõudma serveri vastused meie RPC väljakutsetele:
self.channel.basic_consume( queue=self.callback_queue, on_message_callback=self.on_response, auto_ack=True)
- Seadistame ühenduse loomise:
def on_response(self, ch, method, props, body)
meetod- Seda meetodit me muutma ei pea, siin kontrollitakse kas saabunud sõnum vastab päringule, mis sellest objektist saadeti.
def call(self, n):
meetod- Genereerime uue identifikaatori sõnumile, mille me serverile saadame:
self.corr_id = str(uuid.uuid4())
- Selle abil saab päringud ning vastused "kokku viia"
- Publitseerime sõnumi
self.channel.basic_publish( exchange='', routing_key=self.routing_key, properties=pika.BasicProperties( reply_to=self.callback_queue, correlation_id=self.corr_id, ), body=str(n))
- exchange väätus peab jääma tühjaks süneks ('') - See määrab selle, et kasutame RabbitMQ default Exchange'i
(AMQP default)
- routing_key on see väärtus, mille varasemalt seadistasime
main
blokki sees. See määrab serveri järjekorra, kuhu sõnum jõuab, ning selle kaudu selle, mis funktsiooni soovime välja kutsuda. - properties kaudu paneme paika ka järjekorra nime, kuhu server peaks meie päringu tulemuse tagasi saatma. See sai seadistatud
__init__
funktsioonis - Sarnaselt, correlation_id määrab selle konkreetse väljakutse sõnimi unikaalse ID (mille me varasemalt genereerisime)
n
on sisend(id) funktsioonile, mille edastame RabbitMQ funkrsioonina.
- exchange väätus peab jääma tühjaks süneks ('') - See määrab selle, et kasutame RabbitMQ default Exchange'i
- Genereerime uue identifikaatori sõnumile, mille me serverile saadame:
Ülesanne 3.3: RPC Serveri implementeerimine
Loome Python RabbitMQ Serveri.
- Tõmmake alla Pythoni rpc kliendi skeleton: rpcservert.py
- PS! "..." koodis tuleb asendada teie poolt kirjutatud koodiga.
- NB! asendage koodis RabbitMQ serveri IP ära!
- Implementeerige programmis puudu olevad osad:
lae_alla_raamat_ja_otsi_sone
meetod.- See on meie RPC meetod/protseduur, mida soovime kliendi rakenduse seest välja kutsuda.
- See meetod saab sisendiks kaks parameetrit:
- raamat:
- sone
- raamat:
- See meetod peaks alla laadima Gutenberg repositooriumist raamatu, mille id on
raamat
muutuja väärtus, ning loendama, mitu korda muutujasone
väärtus leidub selles raamatus. - Saate uuesti ära kasutada funktsiooni, mille tegiste esimeses praktikumis.
- Tulemuseks peaks olema üks int tüüpi väärtus.
-
def on_request(ch, method, props, body)
meetod:- see meetod sisaldab serveri koodi RabbitMQ sõnumite kuulamiseks, lae_alla_raamat_ja_otsi_sone funktsiooni välja kutsumiseks ning tulemuse tagasi saatmiseks RabbitMQ kaudu
on_request(ch, method, props, body)
meetod käivitatakse iga unikaalse MQTT sõnumi peal.argumendid
muutuja sisaldab sõnumi sisu, mis on konmverteeritud python sõnaraamatu objektiks. Ehk selleks objektiks, mida klient saatis ning mis sisaldab sõne ja raamatu id väärtust.- Kutsuge välja
lae_alla_raamat_ja_otsi_sone
õigete argumentidega, tulemuseks on muutujaresponse
väärtus - Publitseerime tulemuse tagasi kliendile:
ch.basic_publish(exchange='', routing_key=props.reply_to, properties=pika.BasicProperties(correlation_id = \ props.correlation_id), body=str(response))
- routing_key väärtuseks seame selle sõnumi saatnud kliendi unikaalse järjekorra nime, mille saame kätte läbi Properties tüüpi
props
muutuja niiprops.reply_to
. Selle reply_to väärtus seadistatakse kliendi programmis, ning pannakse sõnumiga kaasa. Tänu sellele teab meie server automaatselt kuhu tulemus tagasi saata.
-
if __name__ == _main_:
meetod:broker_port
väärtus peaks olema see port, mida te saite eelmise praktikumi käigus (56YZ) ning mille ka kliendi rakenduses seadistasite.queue
väärtuseks seadistake funktsiooni nimi, mida see server serveerib.- See peab olema sama mis kliendi pool on seadistatud routing_key väärtuseks!
- Loome ühenduse:
connection = pika.BlockingConnection( pika.ConnectionParameters(host=broker_host, port=broker_port, credentials=credentials, virtual_host= "hspraks03"))
- virtual_host on siin varasemalt loodud virtuaalse keskkonna nimi, sama mis kliendi koodis
- Ka ülejäänud väärtused on samad eelmise praktikumi ja kliendi koodis kasutatavatele väärtustele.
- Loome uue kanali ning asume kuulama sisse tulevaid sõnumeid:
channel = connection.channel() channel.queue_declare(queue=queue) channel.basic_qos(prefetch_count=1) channel.basic_consume(queue=queue, on_message_callback=on_request)
- Iga sisse tuleva sõnumi peal sellesse järjekorda kutsutakse välja
on_request()
meetod.
- Iga sisse tuleva sõnumi peal sellesse järjekorda kutsutakse välja
Ülesanne 3.4: RPC lahenduse testimine
Testige, et õnnestub Nii serverit kui ka klienti tööle panna
- Pange paika Keskkonna muutujad parooli ning kasutajanime jaoks mõlema programmi jaoks!
- PyCharm programmis saate seda teha
Run Configurations
vormi kauduEnvironment Variables
välja kaudu - Linux Käsureal: Enne programmi jooksutamist saate muutujaid paika panna nii:
export MUUTUJA=VÄÄRTUS
- Windows käsureal: Enne programmi jooksutamist saate muutujaid paika panna nii:
set MUUTUJA=VÄÄRTUS
- PyCharm programmis saate seda teha
- Käivitage kõigepealt server
- Seejärel käivitage klient
- Kontrollige, et ei teki kummaski veateateid.
- Klient peak suhteliselt kiiresti tulemuse tagastama ning programmist väljuma.
- Tehke ekraanivaated olukorrast kus on näha, et õnnestus nii serveri kui klienti korrektselt käivitada
Lahenduse esitamine
Praktikumi lahendusena tuleb esitada:
- Kood ja näidisväljundid (teksti või ekraanivatena) loodud programmidest 3.2 ja 3.3 ülesannetes
- Ekraanivaated ülesandes 3.4
- Failid tuleks kokku pakkida üheks Zip failiks enne üles laadimist.
Võimalikud probleemid ja nende potentiaalsed lahendused.
- Kui saate veateate
pika.exceptions.IncompatibleProtocolError: StreamLostError: ('Transport indicated EOF',)
- Siis tuleks kontrollida kas kasutate õiget porti (56YZ) (Ei tohi kasutada adminisitreerimise veebiliidese porti)
- Kui klient jääb ootama ning ei paista midagi tagastavat:
- Uuriga RabbitMQ administreerimise liideses, kas ja kuhu (Exchange, queue) andmed kohale jõuavad
- Kontrollige üle kas marsruutimise võtmed ja queue väärtused on korrektsed.
- Kontrollige üle, et exchange väärtus on koodis tühi sõne ("")