Sunteți pe pagina 1din 13

Eugen Zaharescu – Sisteme distribuite

Google Code University


Introducere în Sistemele Distribuite

Definiţia în cascadă a sistemelor distribuite


Programul reprezintă codul conceput şi scris de programator.
Procesul este creat de un program în momentul rulării sale.
Mesajul este utilizat pentru realizarea comunicaţiei între procese.
Pachetul este un fragment dintr-un mesaj care esre transmis pe o cale de telecomunicaţie.
Protocolul este o descriere formală a formatelor de mesaje și regulile pe care două procese
trebuie să le urmeze pentru a face schimb de aceste mesaje.
Rețeaua este infrastructura prin care se conectează calculatoare, stații de lucru, terminale,
servere, etc. Se compune din routerele care sunt conectate prin legături de comunicație.
Componenta poate fi un proces sau orice element de hardware necesar pentru a rula un proces
sau pentru a realiza comunicațiile între procese, stocarea de date, etc.
Sistemul Distribuit reprezintă un sistem informatic care utilizează un set de protocoale pentru
a coordona acțiunile mai multor procese într-o rețea, astfel încât toate componentele să
coopereze împreună pentru a realiza un singur obiectiv sau un ansamblu redus de sarcini
conexe.

Figura 1. Conceptele de Sisteme Distribuite, Sisteme Paralele, Sisteme Concurente

-1-
Eugen Zaharescu – Sisteme distribuite

Proiectarea şi construirea sistemelor distribuite este justificată de o mulțime de avantaje


printre care şi posibilitatea de a conecta utilizatori distribuiţi la distanță, cu resurse distribuite
la distanță într-un mod deschis și scalabil.
 Atributul deschis se referă la faptul că fiecare componentă este în permanență deschisă
pentru interacțiunea cu alte componente.
 Atributul scalabil se referă la faptul că sistemul poate fi ușor modificat pentru a permite
modificarea numărului de utilizatori, resurse și entități de calcul.
Astfel, un sistem distribuit poate fi mult mai mare și mai puternic, având în vedere
capacitățile combinate ale componentelor distribuite, decât combinațiile de sisteme autonome.
Dar nu e ușor - pentru un sistem distribuit pentru a fi util, acesta trebuie să fie de încredere.
Acesta este un obiectiv dificil de realizat din cauza complexității interacțiunilor dintre
componentele care rulează simultan.
Pentru a fi cu adevărat de încredere, un sistem distribuit trebuie să aibă următoarele
caracteristici:
 Fault-Tolerant (Tolerant la erori): Se poate recupera din erorile generate de componente
fără a efectua acțiuni incorecte.
 Highly Available (Foarte disponibil): Se pot restaura operații, permițându-i acestuia să
reia furnizarea de servicii chiar și atunci când unele componente au eșuat sau au generat
erori.
 Recoverable (Recuperabil): Componentele care eşuează se pot reporni singure și reintră
în sistem, după ce cauza eșecului a fost reparată/ eliminată.
 Consistent (în concordantă): Sistemul poate coordona acțiunile unor componente
multiple, de multe ori în prezența accesului concurent la resurse și/sau a
eșecului/defecţiunii componentelor. Acest lucru stă la baza capacității unui sistem
distribuit de a acționa ca un sistem non-distribuit.
 Scalable: Sistemul poate funcționa corect, chiar și atunci când diferite aspecte/
componente al sistemului sunt scalate/recalibrate la o dimensiune mai mare. De exemplu,
am putea mări dimensiunea rețelei pe care sistemul se execută. Acest lucru crește
frecvența de întreruperi ale rețelei și ar putea degrada un sistem de "non-scalabil". În mod
similar, am putea mări numărul de utilizatori sau servere sau încărcarea totală a
sistemului. Într-un sistem scalabil, acest lucru nu ar trebui să aibă un efect semnificativ.
 Predictable Performance (Performanță previzibilă): Posibilitatea de a oferi capacitatea
de reacție dorită, în timp util.
 Secure (Securitate): Sistemul autentifică orice acces la date și servicii [1]
Acestea sunt standarde ridicate, care reprezintă o provocare pentru a atingerea lor. Probabil
cea mai dificilă provocare este aceea că un sistem distribuit trebuie să fie în măsură să
continue să funcționeze corect, chiar și atunci când componentele eșuează. Această problemă
este discutată în următorul extras dintr-un interviu cu Ken Arnold. Ken este un om de stiinta
de cercetare de la Sun Systems şi este unul dintre arhitecții originali ai lui Jini, și a fost un
membru al echipei de arhitectura care a proiectat CORBA (Common Object Request Broker
Architecture).

Eșecul este diferența definitorie dintre programarea distribuită și locală, astfel încât trebuie să
proiecteze sisteme distribuite luînd în considerearea posibilitatea de eșec. Imaginați-vă că
întrebaţi oamenii: "În cazul în care probabilitatea de apariţie a unui eveniment este de unul la

-2-
Eugen Zaharescu – Sisteme distribuite

1013, cât de des se poate întâmpla acel eveniment?" Răspunsul de bun-simț ar fi,
"Niciodată!." Acesta este un număr infinit de mare, în termeni umani. Dar, dacă întrebi un
fizician, ea ar spune: "Tot timpul. Într-un metru cub de aer, aceste lucruri se întâmplă tot
timpul."
Atunci când proiectați sisteme distribuite, trebuie să spui, "Eșecul se întâmplă tot timpul."
Deci, atunci când proiectați, să proiectați pentru eșec. Aceasta este prima preocupare. Ce
înseamnă proiectare pentru eșec?
O problema clasică este eșecul parțial. Dacă trimit un mesaj pentru tine și apoi are loc o
eroare de rețea, există două rezultate posibile. Una dintre ele este că mesajul ajuns la tine, și
apoi rețeaua s-a defectat, iar eu pur și simplu nu am primit răspunsul. Cealaltă posibilitate este
că mesajul nu a ajuns niciodată la tine, deoarece rețeaua s-a defectat înainte de sosirea
mesajului.
Așa că, dacă nu am primit niciun răspuns, cum știu care dintre cele două rezultate s-a
întâmplat? Nu pot determina cu exactitate sursa erorii până când în, cele din urmă că,
destinatarul va fi găsit. Rețeaua trebuie să fie reparată sau trebuie să vă reveniţi din eroare,
pentru că poate că ceea ce s-a întâmplat nu a fost o eroare de rețea, ci faptul că aplicaţia
dumneavoastră client s-a închis. Cum schimbă aceast lucru modul în care este proiectat
sistemul? Pentru un singur lucru, se pun o multitudine de probleme faţă de soluţiile clasice.
Cu cat sunt mai multe obiective pe care le pot face cu tine, cu atât sunt mai multe lucruri care
trebuie gândite şi proiectate astfel încât să se poate realiza recuperarea în caz de eşec. [2]

Gestiunea eșecurilor/erorilor este o temă importantă în proiectarea sistemelor distribuite.


Eșecurile/erorile se împart în două categorii evidente: hardware și software. Erorile de
hardware au fost o preocupare dominantă, până la sfârșitul anilor 80, dar de atunci fiabilitatea
hardware internă s-a îmbunătățit enorm. Scăderea continuă a energiei termice emise și a
consumului global de energie electrică a circuitelor integrate de dimensiuni din ce în ce mai
mici, reducerea conexiunilor off-chip și a dimensiunii cablurilor de conectare, precum și
tehnici de fabricație de înaltă calitate au jucat toate un rol pozitiv în îmbunătățirea fiabilității
hardware-ului. Astăzi, problemele sunt cel mai adesea asociate cu conexiuni și dispozitive
mecanice, adică defecțiuni de rețea și defecţiuni ale driverelor.
Erorile de software sunt o problemă importantă în sistemele distribuite. Chiar si cu teste
riguroase, bug-urile software-ul reprezintă o fracțiune substanțială de întreruperi neplanificate
(estimate la 25-35%). Bug-uri reziduale în sistemele distribuite mature pot fi clasificate în
două categorii principale [5]:
1. Heisenbug: Un bug care pare să dispară sau modifica caracteristicile sale atunci când este
observat sau cercetat. Un exemplu comun este un bug care apare într-o compilare release-
mode a unui program, dar nu apare atunci când cercetat şi verificat în modul debug-mode.
Numele "Heisenbug" este un joc de cuvinte bazat pe „principiul incertitudinii al lui
Heisenberg” un termen din fizica cuantică, care este de obicei folosit (dar nu foarte exact),
pentru a se referi la modul în care observatorii înşisi afectează măsurătorile lucrurilor pe
care ei le observă, doar prin simplul act de observare (aceasta este de fapt efectul de
observator, și este frecvent confundat cu principiul incertitudinii al lui Heisenberg).

2. Bohrbug: Un bug (numit după modelul atomic dat de Bohr), care, spre deosebire de un
heisenbug, nu dispare sau nu modifică caracteristicile sale în cazul în care este cercetat.
Un Bohrbug de obicei se manifestă în mod fiabil în conformitate cu un set bine definit de
condiții. [6]

-3-
Eugen Zaharescu – Sisteme distribuite

Heisenbugs tind să fie mai răspândite în sistemele distribuite decât în sistemele locale. Un
motiv pentru aceasta este dificultatea pe care o au programatorii în obținerea unei imagini
coerente și cuprinzătoare a interacțiunilor proceselor concurente.
Detalii specifice cu privire la tipurile de defecțiuni care pot apărea într-un sistem distribuit:
 Halting failure (Defecțiuni de staționare/ oprire): O componentă pur și simplu se oprește.
Nu există nici o modalitate de a detecta eșecul decât prin timeout: ea, fie nu mai trimite
"Eu sunt în viață" (bătăilor inimii), mesaje sau nu răspunde la solicitări. Îngheţarea/oprirea
completă a calculatorului este o defecțiune de staționare/ oprire .
 Fail-stop (Oprire de avarie): Este o defecțiune de staționare/ oprire urmată de un fel de
notificare către alte componente. Un server de fișiere de rețea spune clienților săi că este pe
cale de să se oprească este o oprire de avarie.
 Erori de omisiune: Imposibilitatea de a primi/ trimite mesaje în primul rând, din cauza
lipsei de spațiu în memoria tampon (buffer), ceea ce determină eliminarea/aruncarea unui
mesaj fără nici o notificare, nici către expeditor, nici către destinatar. Acest lucru se poate
întâmpla atunci când routerele devin supraîncărcate.
 Network failures (defecțiuni de rețea): întreruperea unei legături de rețea.
 Network partition failure (eroare de partiție de rețelei): Rețeaua se fragmentează în două
sau mai multe sub-rețele disjuncte în care mesajele pot fi trimise, dar între care mesajele
sunt pierdute. Acest lucru poate apărea din cauza unei defecțiuni de rețea .
 Timing failures (eroare de sincronizare):O proprietate temporală a sistemului este
încălcată. De exemplu, ceasurile de pe calculatoare diferite, care sunt folosite pentru a
coordona procesele nu sunt sincronizate; atunci când un mesaj este întârziat mai mult
decât o perioadă limită, etc.
 Byzantine failures (erori bizantine): Cuprind mai multe tipuri de comportamente
defectuoase, inclusiv corupere/ alterare de date sau pierderi, erori cauzate de programe rău
intenționate (malicious programs), etc. [1]
Proiectarea unui sistem distribuit cu caracteristicile enumerate mai sus (cu toleranță la erori,
foarte disponibil, recuperabil din stările de eroare, etc.), înseamnă, de fapt, realizarea unui
proiect care să trateze corespunzător erorile şi să le evite. Proiecta orientată spre tratarea
erorilor, înseamnă eliminarea oricăror presupuneri cu privire la fiabilitatea componentelor
unui sistem.
Oricine construieşte pentru prima dată un sistem distribuit, va face următoarele opt ipoteze.
Acestea sunt atât de bine cunoscute în acest domeniu încât sunt denumite în mod uzual "Cele
8 sofisme":
1. Rețeaua este de încredere/ sigură.
2. Latenţa este zero.
3. Lățimea de bandă este infinită.
4. Rețeaua este securizată.
5. Topologia nu se schimba.
6. Există un singur administrator.
7. Costul de transport este zero.
8. Rețeaua este omogenă. [3]
Latența (Latency): timpul dintre inițierea unei cereri de date și începutul transferului efectiv
de date.

-4-
Eugen Zaharescu – Sisteme distribuite

Lățimea de bandă (Bandwidth): O măsură a capacității unui canal de comunicații. Cu cât este
mai mare lățimea de bandă a unui canal, cu atât mai multe informații se pot transporta prin el.
Topologia (Topology): Diferitele configurații care pot fi adoptate în construirea de rețele, cum
ar fi un inel, bus, stea sau rețea de tip plasă (mesh).
Rețea omogenă (Homogeneous network): o reţea unde funcţionează un singur protocol de
reţea.

Proiectarea unui sistem distribuit


Construirea unui sistem distribuit fiabil, care rulează într-o rețea de comunicații nesigură pare
a fi un obiectiv imposibil. Suntem obligați să ne ocupăm de gestionarea incertitudinilor.
Un proces îşi cunoaște propria stare, și știe de asemenea în ce stare s-au aflat alte procese în
ultimul timp. Dar procesele nu au nici un fel de modalitate de a cunoaște starea curentă a
celorlalte procese. Lor le lipsește echivalentul memoriei partajate.
De asemenea, ele nu dispun de modalități precise de a detecta eșecurile, sau pentru a face
distincţia dintre un eșec/ eroare software/ hardware local/ă de un eșec eșec/ eroare de
comunicaţie.
Proiectarea sistemelor distribuite este, evident, un efort provocator. Cum se poate realiza un
astfel de obiectiv atât de complex fără libertatea de a presupune nimic?.
Vom limita domeniul de aplicabilitate utilizănd numai un anumit tip de proiectare a sistemelor
distribuite, şi anume modelul client-server care foloseşte de cele mai multe ori protocoalele
standard. Se dovedește că aceste protocoale standard oferă un ajutor considerabil referitor la
detaliile de nivel scăzut ale comunicațiilor sigure prin de rețea, ceea ce simplifică activitatea
de implementare. În continuare vor fi prezentate tehnologia client-server și protocoalele de
comunicație.

Arhitectura client-server
În aplicațiile client-server, serverul oferă unele servicii, cum ar fi procesarea interogărilor
asupra bazelor de date sau transmiterea mai departe a prețurile acțiunilor curente cotate şi
citite de pe bursele de valori. Clientul folosește serviciul furnizat de server, fie pentru
afișarea rezultatelor interogării bazei de date în secţiunea utilizator, fie pentru a face
recomandări de cumpărare a unei anumite acțiuni unui investitor. Comunicarea care are loc

-5-
Eugen Zaharescu – Sisteme distribuite

între client și server trebuie să fie sigură. Cu alte cuvinte, nu există date la care se poate
renunţa. De asemenea, ele trebuie să ajungă pe partea de client, în aceeași ordine în care
serverul le-a trimis.
Există mai multe tipuri de servere pe care le întâlnim într-un sistem distribuit. De exemplu:
 Serverele de fișiere care realizează managementul unităților de stocare pe disc pe
care se află sistemele de fișiere.
 Serverele de baze de date care găzduiesc bazele de date și trebuie să le pună la
dispoziția clienților.
 Serverele de nume de rețea implementează o mapare între un nume simbolic sau o
descriere de serviciu Web și o valoare, cum ar fi adresa IP și numărul de port pentru
un proces care furnizează serviciul.

În cazul sistemelor distribuite, pot exista mai multe servere ale unui anumit tip, de exemplu:
 multiple servere de fișiere sau
 mai multe servere de nume de rețea.
Serviciul. Termenul serviciu este utilizat pentru a desemna un set de servere de un anumit tip.
Se spune că o conexiune are loc atunci când un proces, care are nevoie să acceseze un
serviciu, devine asociat cu un anumit server care furnizează serviciul.
Politici de conectare care definesc modul în care este ales un anumit server.
 Politica de localizare: această politică ar putea să se bazeze pe localizarea serverului
(un client Unix NIS- Network Information Service începe prin căutarea unui server în
primul rând pe propria mașină);
 Politica raportului de încărcare se bazează pe raportul de încărcare a serverului (un
server CICS - Customer Information Control System este legat în așa fel încât să aibă o
capacitate de reacție uniformă pentru toți clienții).
Un serviciu distribuit poate folosi:
 Replicarea datelor (data replication), caz în care un serviciu deține mai multe copii
ale datelor pentru a permite accesul local la mai multe locații, sau pentru a crește
disponibilitatea atunci când un proces server se poate fi defecta;
 Caching-ul este un concept asociat și foarte frecvent în sistemele distribuite. Un
proces poate deţine date memorate în cache. În acest caz el păstrează o copie a datelor
la nivel local, pentru un acces mai rapid în situaţia în care este nevoie din nou de ele.
Un cache hit este atunci când o cerere este satisfăcută din datele stocate în memoria cache,
mai degrabă decât de la serviciul primar. De exemplu, browserele folosesc cacheing-ul
documentelor pentru a accelera accesul la documentele utilizate în mod frecvent.
Caching-ul este similar cu replicare, dar datele stocate în memoria cache pot deveni vechi şi
neactualizate. Astfel, s-ar putea să fie nevoie de o politică pentru validarea unui element de
date din memoria cache, înainte de utilizarea acestuia. În cazul în care memoria cache este
actualizată în mod activ de către serviciul primar, cache-ul este identic cu replicarea. [1]
Comunicarea client – server
Așa cum am menționat anterior, comunicarea între client și server trebuie să fie sigură.
Suita Internet Protocol (IP) este setul de protocoale de comunicaţie care permit comunicarea
prin Internet și în majoritatea rețelelor comerciale.

-6-
Eugen Zaharescu – Sisteme distribuite

Transmission Control Protocol (TCP) este unul dintre protocoalele din nucleul acestei suite.
Folosind TCP, serverele și clienții pot crea conexiuni între ei, prin care se pot face schimb de
date în pachete. Protocolul asigură livrarea sigură și în ordine a datelor de la expeditor la
destinatar.
Suita IP poate fi privită ca un set de nivele/ straturi, fiecare nivel având proprietatea de a
folosi numai funcțiile nivelului de mai jos, și exportă funcționalitate numai nivelului de mai
sus. Un sistem care implementează comportamentul acestui protocol alcătuit din mai multe
nivele/straturi este cunoscut sub denumirea de stivă de protocoale.
Stivele de protocoale pot fi implementate fie în hardware, fie în software, fie printr-un
amestec din ambele soluţii. De obicei, numai nivelele/straturile inferioare sunt implementate
în hardware, în timp ce nivelele/straturile superioare sunt implementate în software.
Există patru nivele în oricare nod din suita IP:
1. Application Layer (Nivelul aplicație): Nivelul aplicație este folosit de cele mai multe
programe care necesită o comunicare prin rețea. Datele sunt transmise din program în jos,
către nivelul inferior următor, într-un format specific aplicației, apoi sunt încapsulate într-
un protocol de nivel/strat de transport. Exemple de astfel de aplicații sunt HTTP, FTP sau
Telnet.
2. Transport Layer (Nivelul transport): Responsabilitățile nivelului de transport includ
transmiterea de mesaje end-to-end independente de rețeaua de bază, precum şi controlul
erorilor, controlul fragmentării și controlul fluxului de mesaje. Transmiterile end-to-end a
mesajelor pe nivelul de transport pot fi clasificate astfel:
 orientate pe conexiune (TCP- Transmission Control Protocol) sau
 fără conexiune (UDP- User Datagram Protocol).

-7-
Eugen Zaharescu – Sisteme distribuite

TCP (Transmission Control Protocol) este cel mai sofisticat protocol dintre cele două
protocoale, oferind o livrare sigură a mesajelor:
 În primul rând, TCP se asigură că acel computer care primește mesajele (receptor) este
gata să accepte date. Acesta folosește o comunicaţie de tip three-packet handshake, în
care atât expeditorul cât și destinatarul (receptorul) sunt de acord că sunt pregătite să
comunice.
 În al doilea rând, TCP se asigură că datele ajung la destinație. În cazul în care receptorul
nu recunoaște un anumit pachet, TCP retransmite în mod automat pachetul de obicei de
trei ori.
 Dacă este necesar, TCP poate împărți, de asemenea, pachetele mari, în pachete mai mici,
astfel încât datele pot călători în mod fiabil între sursă și destinație.
 TCP elimină pachetele duplicat și rearanjează pachetele care sosesc în afara secvenței şi
nu respectă ordinea de transmitere.

UDP(User Datagram Protocol) este similar cu TCP (Transmission Control Protocol) prin
aceea că este un protocol pentru trimiterea și primirea de pachete într-o rețea, dar cu două
diferențe majore:
i. În primul rând, este un protocol fără conexiune. Acest lucru înseamnă că un singur
program poate trimite fără conexiune o încărcătură de pachete către un alt program, dar
acesta este sfărşitul relaţiei lor. Al doilea program ar putea trimite unele date înapoi
primului program, iar primul la rândul său, ar putea trimite mai multe date
suplimentare, dar nu există niciodată o legătură solidă între ele.

-8-
Eugen Zaharescu – Sisteme distribuite

ii. UDP este de asemenea diferit de TCP prin faptul că nu oferă nici un fel de garanție că
receptorul va primi pachetele în ordinea corectă în care ele sunt trimise. Tot ceea ce
este garantat este conținutul pachetului. Acest lucru înseamnă că este mult mai rapid,
pentru că nu există nici întârzieri suplimentare pentru verificarea erorilor asupra
pachetelor de date. Din acest motiv, jocurile în reţea folosesc adesea acest protocol. Într-
un joc, în cazul în care un pachet pentru actualizarea unei poziții de ecran lipseşte,
player-ul se va mişca doar un pic. Celelalte pachete vor actualiza pur şi simplu poziţia,
iar pachetul lipsă - cu toate că va face mișcarea un pic mai brusc - nu va schimba nimic.

Cu toate că TCP este mult mai fiabil decât UDP, protocolul prezintă în continuare riscul de a
da erori în mai multe moduri. TCP utilizează și confirmările de retransmisie pentru a detecta
și a repara pierderile de date.
Dar nu poate suplini întreruperile de comunicare mai lungi care deconectează expeditorul de
receptor suficient de mult timp, astfel încât să anihileze strategia de retransmisie. În mod
normal timpul maxim de deconectare este cuprins între 30 și 90 de secunde. TCP ar putea
semnala o eroare și să reia transmiterea atunci când ambele puncte finale sunt din nou în stare
de funcţiune. Acesta este doar un exemplu de modul în care TCP poate eșua, chiar dacă oferă
câteva strategii de atenuare a cazurilor de eroare.
3. Network Layer (Nivelul rețea): După cum este definit inițial, nivelul rețea rezolvă problema
transmiterii pachetelor de date într-o singură rețea. Odată cu apariția conceptului de
Internetworking, s-a adăugat o funcționalitate suplimentară acestui nivel, și anume obținerea
de date dintr-o rețea sursă şi transmiterea lor către o rețea destinație. Acest lucru implică, în
general, rutarea pachetelor într-o rețea de rețele, de exemplu Internetul. IP îndeplinește
sarcina de bază de a transmite pachete de date de la sursă la destinație.
4. Link Layer (Nivelul conexiune): Nivelul conexiune se ocupă de transmiterea fizică a datelor,
și implică de obicei plasarea cadrelor de antet şi a cadrelor terminale în pachetele de date care
traversează rețeaua fizică și interacţionează cu componentele fizice de-a lungul drumului.

Remote Procedure Calls


Multe sisteme distribuite au fost construite folosind protocolul TCP/IP ca fundament pentru
comunicarea între componente. În timp, lucrurile au evoluat şi a apărut o metodă eficientă
pentru interacțiunea clienților cu serverele numită RPC (Remote Procedure Call). Este o
tehnică puternică bazată pe extinderea noțiunii de apel a procedurilor locale, astfel încât
procedura apelată este posibil să nu existe în același spațiu de adrese cu procedura apelantă.
Cele două procese pot fi (rula) pe același sistem, sau pot fi pe sisteme diferite, cu o conexiune
de rețea între ele.

Un apel RPC este similar cu un apel de funcție. Ca şi în cazul unui apel de funcție, atunci
când se face un apel RPC, argumentele sunt transmise procedurii la distanță și apelantul
așteaptă ca un răspuns să-i fie returnat. În ilustrația de mai jos, clientul face un apel de
procedură, care trimite o cerere la server. Procesul client așteaptă până când, fie primește un
răspuns de la server, fie se depăşeşte timpul limită (time out). În cazul în care cererea ajunge
la server, acesta solicită o rutină de expediere care efectuează serviciul solicitat, și trimite
răspunsul la client. După ce apelul RPC este finalizat, procesul client continuă mai departe.

-9-
Eugen Zaharescu – Sisteme distribuite

Firele de execuţie sunt uzuale în sistemele distribuite bazate pe RPC. Fiecare cerere de intrare
la un server, de obicei deschide un nou fir de execuţie. Un fir de execuţie în secţiunea client
emite de obicei un RPC și apoi se blochează (aşteaptă răspunsul). Atunci când este primit
răspunsul, firul de execuţie client îşi reia execuţia.

Un cod bazat pe RPC scris de programator face următoarele trei lucruri:


1. Specifică protocolul de comunicare client-server
2. Dezvoltă programul care rulează pe secţiunea client
3. Dezvoltă programul care rulează pe secţiunea server
Protocolul de comunicare este creat din fragmente (stubs) generate de un compilator de
protocol. Un fragment este o rutină care, de fapt, nu face decât să se declare pe sine și
parametrii pe care îi acceptă. Fragmentele conțin doar un cod suficient pentru a permite ca
acestea să fie compilate și legate între ele.
Programele client și server trebuie să comunice prin intermediul procedurilor și a tipurilor de
date specificate în protocol. Partea de server înregistrează procedurile care pot fi apelate de
către client și primește și returnează datele necesare pentru prelucrare. Partea de client solicită
procedura de la distanță, transmite toate datele necesare și primește datele returnate.
Astfel, o aplicație RPC utilizează clasele generate de generatorul de fragmente pentru a
executa un RPC și așteaptă să se termine. Programatorul trebuie să furnizeze clasele de pe
partea de server care furnizează logica pentru manipularea unei cereri RPC.
RPC introduce un set de cazuri de eroare care nu sunt prezente în programarea procedurală
locală:
 Eroarea de legare (binding error ) poate să apară atunci când clientul porneşte dar
serverul nu rulează.
 Nepotrivirile de versiune (Version mismatches ) apar în cazul în care un client a fost
compilat cu o versiune a unui server, dar serverul a fost actualizat la o versiune mai
nouă.
 Eroarea de timeout poate rezulta dintr-o oprire totală a serverului, dintr-o problemă de
rețea sau o problemă pe un computer client.

Unele aplicații RPC văd aceste tipuri de erori ca iremediabile. Cu toate acestea, sistemele de
tolerante la erori, au surse alternative pentru serviciile critice și în caz de avarie (fail-over) de
la un server primar la un server de backup.

-10-
Eugen Zaharescu – Sisteme distribuite

Un caz complicat de manipulare a erorilor apare atunci când un client are nevoie să cunoască
rezultatul unei cereri, în scopul de a face pasul următor, după o eroare de server. Acest lucru
poate duce uneori la acțiuni şi rezultate incorecte. De exemplu, să presupunem că un proces
client cere unui server de vânzare de bilete să verifice existenţa unui loc în secţiunea
orchestra a sălii Carnegie Hall. Dacă este disponibilă, serverul înregistrează cererea și
vânzarea biletului. Dar cererea eșuează prin depăşirea timpului limită (time out). A fost
scaunul disponibil și vânzarea a fost înregistrată? Chiar dacă există un server de backup
pentru care solicitarea poate fi re-emisă, există riscul ca acelui client să-i fi vândut două bilete,
ceea este o greșeală scumpă în Carnegie Hall [1].
Iată câteva condiții comune generatoare de erori care trebuie controlate şi manipulate:
 Pierderi de date în rețea rezultate din retransmiteri: De multe ori, un sistem încearcă
să realizeze "cel mult o singură" încercare de transmitere a datelor. În cel mai rău caz, în
cazul în care apar transmisii duplicat, se va încerca să se reducă la minimum orice
deteriorare produsă de către datele multiple (primite de mai multe ori).
 Procesul de pe Server se blochează în timpul operaţiilor RPC: În cazul în care un
proces server se blochează/ se opreşte înainte de finalizarea obiectivelor acestuia,
sistemul de obicei se reface corect, deoarece clientul va iniția o nouă solicitare/ cerere de
îndată ce serverul şi-a revenit. În cazul în care serverul se blochează finalizându-şi
obiectivele, dar înainte de a trimite răspunsul RPC, rezultă uneori cereri duplicat din cauza
reîncercărilor primite de la client.
 Procesul Client se blochează înainte de a primi răspuns: Clientul este repornit. Server
elimină răspunsul cu datale solicitate.

Principii de proiectare a Sistemelor Distribuite

 După cum Ken Arnold spunea: "Trebuie să se proiecteze sistemele distribuite din
perspectiva erorilor care pot să apară”. Evitați să faceți presupuneri că vreo componentă
din sistem este într-o stare particulară. Un scenariu de eroare clasic este asociat unui
proces care trimite date unui alt proces care rulează pe o altă mașină. Procesul de pe prima
mașină primește unele date înapoi și le procesează, și apoi trimite rezultatele înapoi la a
doua mașină, presupunând că este gata să primească. Un număr nedefinit de lucruri ar
putea eșua între timp și procesul de emiţător trebuie să anticipeze toate aceste erori
posibile.
 Scenariile de eșec trebuie definite explicit și trebuie să se identifice modul probabil în care
s-ar putea să apară fiecare dintre ele. Codul trebuie scris în aşa fel încât să acopere bine
cele mai probabile scenarii.
 Atât clienții cât și serverele trebuie să fie în măsură să se trateze cazurile cu expeditori/
receptoari care nu răspund.
 Trebuie analizate cu atenție dimensiunea datelor care se trimit prin rețea. Traficul de date
trebuie redus la minimul posibil.
 Întârzierea este timpul dintre inițierea unei cereri de date și începutul transferului efectiv
de date. Minimizarea întârzierilor se reduce, uneori, la alegerea între două variante: mai
multe apeluri/ transferuri mici de date sau un singur apel / transfer mare de date. O
modalitate de selecţie a unei decizii este experimentul. Trebuie realizate mici teste pentru a
identifica cel mai bun compromis.

-11-
Eugen Zaharescu – Sisteme distribuite

 Nu trebuie presupus că datele trimise printr-o rețea (sau chiar trimise de pe un disc pe alt
disc instalate în acelaşi rack) sunt este aceleași date cu cele care ajung la destinaţie. Pentru
sigurantă, trebuie să se facă checksum sau verificări de validitate a datelor pentru a
verifica dacă datele nu s-au schimbat.
 Memoriile cache și strategiile de replicare sunt metode de control a stării componentelor.
Se încearcă să se reducă la minimum componentele dependente de stare în sistemele
distribuite, dar aceasta rămâne o provocare. Starea este menţinută într-un singur loc,
pentru un anume proces, care se găseşte într-un alt loc, ceva ce nu poate fi reconstruit de
orice alte componente. În cazul în care aceasta poate fi reconstruită atunci este o memorie
cache. Memoria cache poate fi de ajutor în diminuarea riscurilor de menținere a stării peste
toate componentele. Dar, datele stocate în memoria cache pot deveni inconsistente,
neactualizate, deci ar fi necesară o politică pentru validarea unui element de date din
memoria cache, înainte de a-l utiliza.
 În cazul în care un proces stochează informații care nu pot fi reconstituite, atunci apar
probleme. O întrebare este posibilă: "Există acum un singur punct de eroare?" Trebuie să
vorbesc cu tine acum - Nu pot vorbi cu nimeni altcineva. Deci, ce se întâmplă dacă te duci
în jos? Pentru a rezolva această problemă, ar putea fi replicat. Strategiile de replicare sunt,
de asemenea, utile în diminuarea riscurilor de menținere a statului. Dar, există provocări și
aici: Ce se întâmplă dacă vorbesc cu un singur Replicant și să modifice anumite date,
atunci eu vorbesc cu altul? Este acea modificare garantată au sosit deja la celalalt? Ce se
întâmplă în cazul în care rețeaua devine partiționate și replicanți nu se poate vorbi unul cu
altul? Poate cineva proceda?
 Există o serie de compromisuri în a decide cum și unde să mențină starea, și când să
utilizeze cache-uri și replicare. Este mult mai dificil de a rula mici teste în aceste scenarii,
din cauza aeriene în stabilirea diferitelor mecanisme.
 În cazul în care un proces stochează informații care nu pot fi reconstituite, atunci apar
probleme. O întrebare este posibilă: "Există acum un singur punct de eroare?" Trebuie să
vorbesc cu tine acum - Nu pot vorbi cu nimeni altcineva. Deci, ce se întâmplă dacă te duci
în jos? Pentru a rezolva această problemă, ar putea fi replicat. Strategiile de replicare sunt,
de asemenea, utile în diminuarea riscurilor de menținere a statului. Dar, există provocări și
aici: Ce se întâmplă dacă vorbesc cu un singur Replicant și să modifice anumite date,
atunci eu vorbesc cu altul? Este acea modificare garantată au sosit deja la celalalt? Ce se
întâmplă în cazul în care rețeaua devine partiționate și replicanți nu se poate vorbi unul cu
altul? Poate cineva proceda?
 Există o serie de compromisuri în a decide cum și unde să mențină starea, și când să
utilizeze cache-uri și replicare. Este mult mai dificil de a rula mici teste în aceste scenarii,
din cauza aeriene în stabilirea diferitelor mecanisme.If a process stores information that
can't be reconstructed, then problems arise. One possible question is, "Are you now a
single point of failure?" I have to talk to you now - I can't talk to anyone else. So what
happens if you go down? To deal with this issue, you could be replicated. Replication
strategies are also useful in mitigating the risks of maintaining state. But there are
challenges here too: What if I talk to one replicant and modify some data, then I talk to
another? Is that modification guaranteed to have already arrived at the other? What
happens if the network gets partitioned and the replicants can't talk to each other? Can
anybody proceed? There are a set of tradeoffs in deciding how and where to maintain
state, and when to use caches and replication. It's more difficult to run small tests in these
scenarios because of the overhead in setting up the different mechanisms.

-12-
Eugen Zaharescu – Sisteme distribuite

 Trebuie avute în vedere creşterea vitezei și performanței sistemelor distribuite. Se vor


determina părțile sistemului distribuit care pot avea un impact semnificativ asupra
performanței: „Unde sunt blocajele (bottlenecks) și de ce apar ele ? Trebuie concepute
teste mici pentru evaluarea alternativelor. Se vor trasa profilele acestor blocaje și se vor
efectua măsurători pentru a afla mai multe.
 Retransmisia este costisitoare. Este important să se experimenteze, astfel încât să se poată
regla întârzierea care solicită o retransmisie să fie optimă.

Exercises
1. Have you ever encountered a Heisenbug? How did you isolate and fix it?
2. For the different failure types listed above, consider what makes each one difficult for a
programmer trying to guard against it. What kinds of processing can be added to a
program to deal with these failures?
3. Explain why each of the 8 fallacies is actually a fallacy.
4. Contrast TCP and UDP. Under what circumstances would you choose one over the other?
5. What's the difference between caching and data replication?
6. What are stubs in an RPC implementation?
7. What are some of the error conditions we need to guard against in a distributed
environment that we do not need to worry about in a local programming environment?
8. Why are pointers (references) not usually passed as parameters to a Remote Procedure
Call?
9. Here is an interesting problem called partial connectivity that can occur in a distributed
environment. Let's say A and B are systems that need to talk to each other. C is a master
that also talks to A and B individually. The communications between A and B fail. C can
tell that A and B are both healthy. C tells A to send something to B and waits for this to
occur. C has no way of knowing that A cannot talk to B, and thus waits and waits and
waits. What diagnostics can you add in your code to deal with this situation?
10. What is the leader-election algorithm? How can it be used in a distributed system?
11. This is the Byzantine Generals problem: Two generals are on hills either side of a valley.
They each have an army of 1000 soldiers. In the woods in the valley is an enemy army of
1500 men. If each general attacks alone, his army will lose. If they attack together, they
will win. They wish to send messengers through the valley to coordinate when to attack.
However, the messengers may get lost or caught in the woods (or brainwashed into
delivering different messages). How can they devise a scheme by which they either attack
with high probability, or not at all?

References
[1] Birman, Kenneth. Reliable Distributed Systems: Technologies, Web Services and
Applications. New York: Springer-Verlag, 2005.
[2] Interview with Ken Arnold
[3] The Eight Fallacies
[4] Wikipedia article on IP Suite
[5] Gray, J. and Reuter, A. Transaction Processing: Concepts and Techniques. San Mateo,
CA: Morgan Kaufmann, 1993.
[6] Bohrbugs and Heisenbugs
http://staff.fmi.uvt.ro/~dana.petcu/distrib/

-13-

S-ar putea să vă placă și