AMQP (Advanced Message Queuing Protocol) este un protocol deschis la nivel de aplicație pentru transmiterea mesajelor între componentele sistemului. Ideea principală este că subsistemele individuale (sau aplicațiile independente) pot face schimb de mesaje în mod arbitrar printr-un broker AMQP care realizează rutarea , eventual garantează livrarea, distribuirea fluxurilor de date, abonamentul la tipurile de mesaje dorite.
Arhitectura protocolului a fost dezvoltată de John O'Hara de la JP Morgan Chase & Co [1] .
AMQP se bazează pe trei concepte:
Protocolul poate fi împărțit în două straturi:
Exemple de coadă:
Protocolul nu se limitează la aceste trei tipuri. Sunt date ca exemplu de implementare.
Primește mesaje de la furnizor și le direcționează către coada de mesaje conform unor criterii predefinite. Astfel de criterii se numesc legături. Schimbul este un mecanism de negociere și rutare a mesajelor. Pe baza mesajelor și a parametrilor acestora (legături), aceștia iau o decizie cu privire la redirecționarea către o coadă sau un alt schimb. Nu stocați mesaje.
Termenul schimb înseamnă algoritm și instanță de algoritm. Ei spun, de asemenea, tip de schimb și instanță de schimb.
AMQP definește un set de tipuri de schimb standard. Aplicațiile își pot crea propria instanță de schimb.
Fiecare schimb implementează propriul algoritm de rutare. Există mai multe tipuri de schimb standard descrise în specificația funcțională a standardului. Dintre acestea, două sunt importante:
Serverul va crea mai multe schimburi, inclusiv directe și tematice. Vor avea nume binecunoscute și aplicațiile client vor putea lucra cu ei.
Fiecare server AMQP pre-creează mai multe instanțe de schimb. Aceste instanțe există atunci când serverul rulează și nu pot fi distruse. Aplicațiile AMQP își pot crea propriile schimburi. AMQP nu folosește metoda create pentru aceasta, în schimb se declară instanța, care urmează logica: „creați dacă nu este creat, continuați altfel”. Se poate spune că crearea schimbului este idempotent . Probabil că aplicațiile vor crea schimburi după cum este necesar și apoi le vor distruge ca fiind inutile. AMQP oferă o metodă de distrugere a schimbului.
În general, schimbul examinează proprietățile mesajului, câmpurile de antet și conținutul corpului său și, folosind acestea și, eventual, date din alte surse, decide cum să direcționeze mesajul. În cele mai multe cazuri simple, Exchange ia în considerare un câmp cheie, pe care îl numim Cheia de rutare . Cheia de rutare este o adresă virtuală pe care serverul de schimb o poate folosi pentru a decide dacă trimite un mesaj. Pentru rutarea punct la punct , cheia de rutare este de obicei numele cozii de mesaje. Pentru rutarea pub-sub , cheia de rutare este de obicei valoarea ierarhiei subiectelor (subiect - vezi publicație/subscruber). În cazuri mai complexe, cheia de rutare poate fi combinată cu rutarea după câmpurile antetului mesajului și/sau conținutul mesajului.
Când o aplicație client creează o coadă de mesaje, poate specifica următoarele proprietăți:
Un mesaj AMQP constă dintr-un set de proprietăți și conținut non-public. Un nou mesaj este creat de producător folosind API-ul client AMQP. Producătorul adaugă conținut mesajului și eventual setează unele proprietăți ale mesajului. Producătorul marchează mesajul cu informații de rutare care arată ca o adresă, dar poate fi orice. Producătorul trimite apoi mesajul la schimb . Când un mesaj ajunge la server, schimbul (de obicei) îl direcționează către un set de cozi care există și pe server. Dacă mesajul nu este rutabil, Exchange îl poate renunța sau îl poate returna în aplicație. Producătorul decide cum să trateze mesajele care nu sunt rutabile.
Un mesaj poate exista în multe cozi de mesaje. Serverul poate gestiona acest lucru în moduri diferite, cum ar fi copierea mesajului utilizând contorizarea referințelor etc. Acest lucru nu afectează interoperabilitatea. Cu toate acestea, atunci când un mesaj este direcționat către mai multe cozi de mesaje, este identic în fiecare coadă de mesaje. Nu există un identificator unic aici care să facă distincția între diferitele copii.
Când un mesaj ajunge în coada de mesaje, acesta încearcă imediat să-l livreze consumatorului prin AMQP. Dacă acest lucru nu este posibil, atunci mesajul este stocat în coada de mesaje (în memorie sau pe disc la cererea producătorului ) și așteaptă ca consumatorul să fie gata. Dacă nu există niciun consumator , atunci coada poate returna un mesaj producătorului prin AMQP (din nou, dacă producătorul a cerut-o).
Când coada de mesaje poate livra un mesaj către consumator , acesta îl elimină din stocarea sa internă. Acest lucru se poate întâmpla imediat sau după ce consumatorul recunoaște că și-a încheiat cu succes munca, a procesat mesajul. Consumatorul alege cum și când mesajele sunt „recunoscute”. consumatorul poate şi respinge mesajul (confirmare negativă).
Mesajele producătorilor și confirmările consumatorilor sunt grupate în tranzacții. Atunci când o aplicație joacă ambele roluri, ceea ce este adesea cazul, ea face o treabă mixtă de trimitere de mesaje și de confirmare, apoi comiterea sau anularea tranzacției.
Livrarea mesajelor de pe server către consumator nu este tranzacțională.
Producer este o aplicație client care publică mesaje pentru schimb .
Prin analogie cu dispozitivul de e-mail, puteți vedea că producătorul nu trimite mesaje direct la coadă (coada de mesaje). Orice alt comportament ar rupe abstracția din modelul AMQ. Acest lucru ar fi similar cu ciclul de viață al unui mesaj de e-mail: rezolvarea e-mailului, ocolirea tabelelor de rutare ale MTA și lovirea directă a cutiei poștale. Acest lucru ar face imposibilă introducerea de filtrare și procesare intermediară, cum ar fi detectarea spam-ului.
Modelul AMQ folosește același principiu ca și sistemul de e-mail: toate mesajele sunt trimise către un singur schimb sau MTA , care inspectează mesajele pe baza unor reguli și informații care sunt ascunse de la expeditor și le direcționează către punctele de distribuție care sunt, de asemenea, ascunse de către expeditor. expeditor. (și le direcționează către punctele de livrare care sunt, de asemenea, ascunse de expeditor - aici punctele de distribuție sunt puncte de livrare din documentație).
Consumer este aplicația client care primește mesaje din coada de mesaje.
Analogia noastră cu e-mailul începe să se defecteze când ne uităm la consumator (destinatari). Clienții de e-mail sunt pasivi - pot citi cutiile poștale, dar nu au niciun efect asupra modului în care sunt populate aceste cutii poștale. Cu AMQP , consumatorul poate fi și pasiv, la fel ca clienții de e-mail. Adică, putem scrie o aplicație care ascultă o anumită coadă de mesaje și pur și simplu procesează informațiile primite. În acest caz, coada de mesaje trebuie să fie gata înainte de a începe aplicația și trebuie să fie „atașată” acesteia.
Consumatorul are, de asemenea, următoarele caracteristici:
Este ca și cum ai avea un sistem de e-mail care, la nivel de protocol, poate:
Majoritatea arhitecturilor de integrare nu au nevoie de acest nivel de complexitate. Majoritatea utilizatorilor AMQP au nevoie de funcționalități de bază din cutie. AMQP oferă acest lucru în următorul mod:
Drept urmare, legarea de bază permite producătorului să trimită mesaje direct în coada de mesaje, emulând astfel cea mai simplă schemă de trimitere a unui mesaj către un receptor la care oamenii s-ar aștepta de la un middleware tradițional.
Legarea de bază nu împiedică utilizarea cozii de mesaje în proiecte mai complexe. Vă permite să utilizați AMQP fără o înțelegere specifică a mecanismelor de legare și schimb.
Secțiunea descrie procesul de interacțiune dintre aplicație și server
Middleware-ul este complex, iar atunci când au proiectat structura protocolului, creatorii săi au încercat să îmblânzească această complexitate. Abordarea lor a fost să modeleze un API tradițional bazat pe clase care conțin metode, fiecare metodă făcând exact un lucru și făcând-o bine. Acest lucru are ca rezultat un set mare de comenzi, dar unul relativ ușor de înțeles.
Comenzile AMQP sunt grupate în clase. Fiecare clasă acoperă o zonă funcțională specifică. Unele clase sunt opționale - fiecare peer implementează clasele pe care trebuie să le suporte.
Există două metode diferite de dialog:
Pentru a simplifica procesarea metodei, definim răspunsuri separate pentru fiecare cerere sincronă. Adică, o metodă nu este utilizată pentru a răspunde la două solicitări diferite. Aceasta înseamnă că un peer, atunci când trimite o solicitare sincronă, poate accepta și procesa metodele primite până când este primit unul dintre răspunsurile sincrone valide. Acest lucru distinge AMQP de protocoalele RPC mai tradiționale.
O metodă este definită formal ca o solicitare sincronă, un răspuns sincron (la o cerere specifică) sau asincron. În cele din urmă, fiecare metodă este definită formal ca pe partea client (adică, server-client) sau pe partea server (client-server).
AMQP este proiectat să fie comparabil cu API-ul middleware. Procesul de potrivire este oarecum intelectual, adică. înțelege că nu toate metodele și nu toate argumentele au sens pentru aplicare, dar este și mecanică, adică. prin stabilirea anumitor reguli, toate metodele pot fi corelate fără intervenție manuală.
Beneficiile acestui lucru sunt că, învățând semantica AMQP, dezvoltatorii vor găsi aceeași semantică furnizată în orice cadru pe care îl folosesc.
Un exemplu de metodă Queue.Declare:
coadă . declara coada = my . coadă automat - ștergere = TRUE exclusiv = FALSPoate fi convertit într-un cadru de rețea:
+--------+---------+----------+-----------+------- ----+ | coada | declara | al meu . coada | 1 | 0 | +--------+---------+----------+-----------+------- ----+ numele metodei clasei automat - șterge exclusivSau într-o metodă API de nivel înalt
coadă . Declara ( "coada.mea" , ADEVARAT , FALS );Metoda asincronă de potrivire logică în pseudocod:
trimite metoda la serverMetoda sincronă de potrivire logică în pseudocod:
trimite metoda de solicitare la server repeta asteptati raspuns de la server dacă răspunsul este o metodă asincronă metoda procesului ( de obicei , conținut livrat sau returnat ) altfel afirmă că metoda este un răspuns valid la cerere ieșire repetă sfârşitul – dacă sfârşit - repetăEste de remarcat faptul că pentru majoritatea aplicațiilor, middleware-ul poate fi complet ascuns în straturile tehnice ale sistemului și că API-ul efectiv utilizat este mai puțin important decât faptul că middleware-ul este robust și funcțional.
Protocolul vorbăreț este lent. Folosim activ asincronia în cazurile în care există o problemă de performanță. Acesta este, de obicei, locul în care trimitem conținut de la un peer la altul. Expediem metodele cât mai repede posibil, fără să așteptăm confirmări. Acolo unde este necesar, implementăm windowing și throttling la un nivel superior, cum ar fi nivelul consumatorului.
Protocolul dispensează de la sesizări, pentru că implementează un model de afirmare pentru toate evenimentele. Ori reușește, ori se aruncă o excepție? care închide un canal sau o conexiune.
Nu există notificări în AMQP. Un eveniment reușit - în tăcere, un eșec - se declară. Când o aplicație are nevoie de urmărire explicită a succeselor și eșecurilor, ar trebui să utilizeze tranzacții.
Conexiunea este concepută pentru a fi durabilă și a gestiona multe canale.
Ciclul de viață al conexiuniiInformațiile nu sunt schimbate pentru erori ale conexiunilor incomplet deschise. Gazda care a întâmpinat eroarea ar trebui să închidă socket-ul fără o notificare ulterioară.
AMQP este un protocol multicanal. Canalele oferă posibilitatea de a multiplexa o conexiune TCP/IP grea în mai multe conexiuni ușoare. Acest lucru face ca protocolul să fie mai „prietenos pentru firewall”, deoarece utilizarea portului este previzibilă. De asemenea, înseamnă că modelarea traficului și alte caracteristici QoS ale rețelei pot fi utilizate cu ușurință.
Canalele sunt independente unele de altele și pot îndeplini diferite funcții simultan cu alte canale, în timp ce lățimea de bandă disponibilă este împărțită între sarcini concurente.
Este de așteptat și încurajat ca aplicațiile client multi-thread să utilizeze adesea modelul „canal-pe-thread” pentru ușurința dezvoltării. Cu toate acestea, deschiderea mai multor conexiuni la unul sau mai multe servere AMQP de la un singur client este, de asemenea, perfect acceptabilă. Ciclul de viață al unui canal este următorul:
Permite unei aplicații să gestioneze instanțe de schimb pe server. Această clasă permite unei aplicații să scrie propriul script de gestionare a mesajelor fără a se baza pe nicio configurație.
Notă: Majoritatea aplicațiilor nu au nevoie de acest nivel de complexitate, iar middleware-ul vechi este puțin probabil să accepte această semantică.
Ciclul de viață al schimbuluiClasa de coadă permite unei aplicații să gestioneze cozile de mesaje pe un server. Acesta este un pas de bază în aproape toate aplicațiile care primesc mesaje, cel puțin pentru a verifica dacă coada de mesaje așteptată există cu adevărat.
Protocolul prevede două cicluri de viață de coadă:
AMQP implementează mecanismul de abonare la subiecte sub formă de cozi de mesaje. Acest lucru permite structuri interesante în care un abonament poate fi echilibrat de încărcare într-un grup de aplicații de abonați cooperanți.
Ciclul de viață a abonamentuluiClasa de bază implementează capabilitățile de mesagerie descrise în această specificație. Suportă următoarea semantică:
AMQP acceptă două tipuri de tranzacții:
Clasa Tranzacție („tx”) oferă aplicațiilor acces la al doilea tip de tranzacție, tranzacțiile cu serverul local. Semantica clasei este următoarea:
Tranzacțiile se referă la publicarea și confirmările de conținut, nu livrarea. Prin urmare, rollback-ul nu renunță la coadă și nu declanșează relivrarea. Clientul poate confirma aceste mesaje la următoarea tranzacție.
Această secțiune explică modul în care comenzile se mapează la protocolul la nivel de fir .
AMQP este un protocol binar. Informațiile sunt organizate în cadre de diferite tipuri. Cadrele conțin metode de protocol și alte informații. Toate cadrele au același format general: antetul cadrului, sarcina utilă și sfârșitul cadrului. Formatul de încărcare a cadrului depinde de tipul de cadru.
La nivel de transport, se presupune utilizarea stivei TCP/IP sau a analogilor.
Într-o singură conexiune priză, pot exista mai multe fluxuri independente de control, numite canale. Fiecare cadru este numerotat cu un număr de canal. Prin intercalarea cadrelor lor, diferitele canale împărtășesc această conexiune. Pentru orice canal dat, cadrele sunt executate într-o secvență strictă care poate fi folosită pentru a conduce un analizor de protocol (de obicei o mașină de stări).
Construim cadre folosind un set mic de tipuri de date, cum ar fi biți, numere întregi, șiruri de caractere și tabele de câmpuri. Câmpurile cadru sunt strâns împachetate, fără a le face să fie lente sau greu de analizat. Este relativ ușor să creați un strat de încadrare mecanic din specificațiile protocolului.
Formatarea la nivel de fir este concepută pentru a fi scalabilă și suficient de versatilă pentru a fi utilizată în protocoale arbitrare de nivel înalt (nu doar AMQP). Anticipăm că AMQP se va extinde, se va îmbunătăți și, în alt mod, se va schimba în timp, iar formatul la nivel de fir va accepta acest lucru.
Tipuri de date AMQP utilizate în cadre:
Clientul și serverul negociază un protocol. Aceasta înseamnă că atunci când un client se conectează, serverul oferă anumite opțiuni pe care clientul le poate accepta sau modifica. Când ambii sunt de acord asupra rezultatului, conexiunea este considerată stabilită. Negocierea este utilă deoarece vă permite să setați presetări de conexiune.
Coordonarea are loc pe mai multe aspecte:
Limitele convenite pot permite ambelor părți să prealoceze tampon de cheie, evitând blocajul. Fiecare cadru primit fie respectă limitele negociate și, prin urmare, este sigur, fie le depășește, caz în care cealaltă parte a eșuat și trebuie dezactivată. Acest lucru se aliniază foarte bine cu filozofia AMQP de „fie funcționează așa cum ar trebui, fie nu funcționează deloc”.
Ambele noduri negociază limite la cea mai mică valoare convenită, după cum urmează:
Stiva TCP/IP - funcționează cu fluxuri, nu are un mecanism de demarcare a cadrelor încorporat. Protocoalele existente rezolvă această problemă în mai multe moduri diferite:
Toate cadrele constau dintr-un antet (7 octeți), o sarcină utilă de dimensiune arbitrară și un octet „sfârșit de cadru” care detectează cadrele malformate:
0 1 3 7 mărime + 7 mărime + 8 +------+---------+-------------+ +------------+ +--- --------+ | tip | canal | dimensiune | | sarcină utilă | | cadru - capăt | +------+---------+-------------+ +------------+ +--- --------+ octet scurtă dimensiune lungă octeți octetCadrul se citește după cum urmează:
În implementările realiste în ceea ce privește performanța, vom folosi „read-ahead buffering” sau „gathering reads” pentru a evita efectuarea a trei apeluri de sistem separate pentru a citi un cadru.
Cadrele de metodă poartă comenzi de protocol de nivel înalt (pe care le numim „metode”). Un cadru de metodă poartă o comandă. Sarcina utilă a cadrului de metodă are următorul format:
0 2 4 +----------+-----------+-------------- - - | clasa - id | metoda - id | argumente ... +----------+-----------+-------------- - - scurt scurt ...Cadrul metodei este tratat astfel:
1. Citirea cadrului metodei de încărcare utilă.
2. Desfacerea sa într-o structură. Această metodă are întotdeauna aceeași structură, așa că o puteți despacheta rapid
3. Verificați dacă această metodă este permisă în contextul actual.
4. Verificarea ca argumentele metodei sunt valide.
5. Executarea acestei metode.
Corpul cadrului metodei este construit ca o listă de câmpuri de date AMQP (biți, numere întregi, șiruri și tabele de șiruri). Codul de marshaling este generat trivial direct din specificațiile protocolului și poate fi foarte rapid.
Conținutul este datele aplicației pe care le transferăm de la client la client prin serverul AMQP. Conținutul este, aproximativ vorbind, un set de proprietăți plus o parte binară a datelor. Setul de proprietăți permise este definit de clasa de bază și formează „cadru antet de conținut”. Datele pot fi de orice dimensiune și împărțite în mai multe (sau mai multe) blocuri, fiecare dintre acestea formând un „schelet al corpului de conținut”.
Privind cadrele pentru un anumit canal pe măsură ce acesta este transmis prin fir, putem vedea ceva de genul acesta:
[ metoda ] [ metoda ] [ antet ] [ corp ] [ corp [ metoda ] ...Unele metode (cum ar fi Basic.Publish , Basic.Deliver , etc.) sunt definite oficial ca purtătoare de conținut. Când un peer trimite un astfel de cadru de metodă, îl urmează întotdeauna cu un antet de conținut și cu sau fără câteva cadre de corp de conținut. Antetul cadrului de conținut are următorul format:
0 2 4 12 14 +----------+--------+-----------+----------------+ ------------- - - | clasa - id | greutate | dimensiunea corpului | steaguri de proprietate | lista de proprietati ... +----------+--------+-----------+----------------+ ------------- - - scurt scurt lung lung scurt rest ...Am pus corpul conținutului în cadre separate (în loc să îl includem într-o metodă), astfel încât AMQP să poată accepta metode „zero copie” în care conținutul nu este niciodată clasificat sau codificat. Punem proprietățile conținutului în propriul lor cadru, astfel încât destinatarii să poată renunța selectiv conținutul pe care nu doresc să îl proceseze.
Heartbeat este o tehnică concepută pentru a suprascrie una dintre caracteristicile TCP/IP, și anume capacitatea sa de a se recupera de la o conexiune fizică întreruptă, închizându-se doar după un timeout destul de lung. În unele scenarii, trebuie să știm foarte repede dacă peer-ul este în jos sau nu răspunde din alte motive (de exemplu, se blochează într-o buclă). Deoarece bătăile inimii se pot face la un nivel scăzut, vom implementa acest lucru ca un tip special de cadru care este schimbat între nodurile la stratul de transport, mai degrabă decât ca o metodă de clasă.
AMQP folosește excepții pentru tratarea erorilor. Orice eroare operațională (coada de mesaje nu a fost găsită, drepturi de acces insuficiente etc.) va declanșa o excepție de canal. Orice eroare structurală (argument prost, secvență proastă a metodei etc.) are ca rezultat o excepție de conexiune. Excepția închide canalul sau conexiunea și returnează un cod de răspuns și un corp de răspuns la aplicația client. Folosim un cod de răspuns din 3 cifre plus schema de text de răspuns care este utilizată în HTTP și multe alte protocoale.
Se spune că conexiunea sau canalul este „deschis” pentru client când trimite un Open și pentru server când trimite un Open-Ok. De acum înainte, un peer care dorește să închidă un canal sau o conexiune trebuie să facă acest lucru folosind protocolul de strângere de mână, care este descris aici.
Închiderea unui canal sau a unei conexiuni din orice motiv – normal sau excepțional – trebuie făcută cu atenție. Închiderile bruște nu sunt întotdeauna detectate rapid și, după o excepție, este posibil să pierdem codurile de răspuns de eroare. Designul corect este de a negocia manual închiderea, astfel încât canalul/conexiunea să fie închisă numai după ce suntem siguri că cealaltă parte este conștientă de situație.
Când un peer decide să închidă un canal sau o conexiune, trimite metoda Close. Nodul receptor trebuie să răspundă la închidere cu un Close-Ok, iar apoi ambele părți își pot închide canalul sau conexiunea. Rețineți că dacă colegii ignoră Close, poate apărea un impas dacă ambii colegi trimit Close în același timp.
Este posibil să citiți și să scrieți cadre AMQP direct din aplicație, dar ar fi un design prost. Chiar și cea mai simplă conversație AMQP este mult mai complexă decât, să zicem, HTTP, iar dezvoltatorii de aplicații nu trebuie să înțeleagă lucruri precum formatele binare de încadrare pentru a trimite un mesaj la o coadă de mesaje. Arhitectura client AMQP recomandată constă din mai multe niveluri de abstractizare:
De asemenea, există de obicei un anumit nivel de I/O, care poate fi foarte simplu (socket sincron de citire și scriere) sau complex (I/O multi-threaded complet asincron). Această diagramă arată arhitectura generală recomandată:
+------------------------+ | aplicare | +-----------+------------+ | +------------------------+ +---| Strat API |---- Strat API client -----+ | +-----------+------------+ | | | | | +-----------------------+ +----------------+ | | | Manager conexiune +----+ Strat de cadru | | | +-----------+------------+ +-------+ | | | | | +-----------------------+ | +---| Strat I / O asincron |--------------------------+ +-----------+------------+ | ------- - - - - Rețea - - - - -------În acest document, când vorbim despre „client API” ne referim la toate straturile de sub aplicație (i/o, încadrare, manager de conexiune și straturi API. De obicei vorbim despre „client API” și „aplicație” ca două lucruri separate în care aplicația folosește API-ul client pentru a vorbi cu serverul middleware.
Un mesaj este unitatea de procesare atomică a unui sistem de rutare și middleware. Mesajele conțin conținut care constă dintr-un antet de conținut care conține un set de proprietăți și un corp de conținut care conține un bloc opac de date binare.
Un mesaj poate corespunde mai multor entități de aplicație diferite:
Mesajele pot fi permanente. Un mesaj persistent este stocat în siguranță pe disc și este garantat că va fi livrat chiar și în cazul unei întreruperi majore a rețelei, a unei defecțiuni a serverului, a depășirii etc.
Mesajele pot avea prioritate. Un mesaj cu prioritate mare este trimis înaintea mesajelor cu prioritate inferioară care așteaptă în aceeași coadă de mesaje. Când mesajele trebuie să fie abandonate pentru a menține un anumit nivel de calitate a serviciului, serverul va renunța mai întâi la mesajele cu prioritate scăzută.
Serverul NU TREBUIE să modifice conținutul mesajelor pe care le primește și le transmite aplicațiilor de consum. Serverul POATE adăuga informații la antetele conținutului, dar NU TREBUIE să elimine sau să modifice informațiile existente.
Gazde virtualeO gazdă virtuală este o secțiune de date dintr-un server, o comoditate administrativă care se va dovedi utilă celor care doresc să ofere AMQP ca serviciu pe o infrastructură partajată.
Gazda virtuală conține propriul spațiu de nume, set de schimburi, cozi de mesaje și toate obiectele asociate. Fiecare conexiune trebuie să fie asociată cu o gazdă virtuală.
Clientul selectează gazda virtuală în metoda Connection.Open după autentificare. Aceasta înseamnă că schema de autentificare a serverului este comună tuturor nodurilor virtuale de pe acel server. Cu toate acestea, schema de autorizare utilizată poate fi unică pentru fiecare gazdă virtuală. Acest lucru ar trebui să fie util pentru o infrastructură de găzduire partajată. Administratorii care necesită scheme de autentificare diferite pentru fiecare gazdă virtuală trebuie să utilizeze servere separate
Toate canalele dintr-o conexiune funcționează cu aceeași gazdă virtuală. Nu există nicio modalitate de a contacta o altă gazdă virtuală pe aceeași conexiune și nu există nicio modalitate de a comuta la o altă gazdă virtuală fără a întrerupe conexiunea și a începe de la capăt.
Protocolul nu oferă niciun mecanism pentru crearea sau configurarea gazdelor virtuale - acest lucru se face într-un mod nespecificat în cadrul serverului și depinde în întregime de implementare.
SchimburiExchange este un agent de rutare a mesajelor în interiorul unei gazde virtuale. Instanța de schimb (la care ne referim în mod obișnuit ca „schimb”) primește mesaje și informații de rutare - în principal cheia de rutare - și fie transmite mesajele către cozile de mesaje, fie către serviciile interne. Schimburile sunt denumite pe bază de gazdă virtuală.
Aplicațiile sunt libere să creeze, să partajeze și să distrugă instanțe de schimb aflate sub autoritatea lor.
Schimburile pot fi permanente, temporare sau șterse automat. Schimburile permanente există până când sunt eliminate. Schimburile temporare există până când serverul este oprit. Schimburile șterse automat există până când nu mai sunt folosite.
Serverul oferă un set specific de tipuri de schimb. Fiecare tip de schimb implementează o mapare și un algoritm specific, așa cum este definit în secțiunea următoare. AMQP prescrie un număr mic de tipuri de schimb și recomandă încă câteva. În plus, fiecare implementare de server poate adăuga propriile tipuri de schimb.
Un schimb poate direcționa un singur mesaj către mai multe cozi de mesaje în paralel. Acest lucru creează mai multe instanțe de mesaj care sunt consumate independent unele de altele.
Tipul de schimb directTipul de schimb direct funcționează astfel:
Serverul TREBUIE să implementeze o schimbare directă și TREBUIE să predefinite în fiecare gazdă virtuală cel puțin două schimburi directe: unul numit amqp.direct și unul fără nume public care servește ca schimb implicit pentru gestionarea metodelor publice.
Rețineți că cozile de mesaje pot fi contactate folosind orice valoare validă a cheii de rutare, dar cel mai adesea cozile de mesaje vor fi contactate folosind propriul nume ca cheie de rutare.
În special, toate cozile de mesaje TREBUIE să fie legate automat la un schimb fără nume public, folosind numele cozii de mesaje ca cheie de rutare.
Tipul de schimb FanoutFanout Exchange funcționează astfel:
Fanout Exchange este banal de proiectat și implementat. Acest tip de schimb și numele predeclarat amq.fanout sunt necesare.
Tipul de schimb de subiecteSchimbul de subiecte funcționează astfel:
Cheia de rutare folosită pentru Topic Exchange trebuie să fie compusă din cuvinte separate prin puncte. Dimensiunea minimă a cuvântului este de 0 caractere. Fiecare cuvânt poate conține literele AZ și az, precum și numerele 0-9.
Modelul de rutare urmează aceleași reguli ca și cheia de rutare, cu adăugarea că * se potrivește cu un cuvânt și # se potrivește cu zero sau mai multe cuvinte. Astfel, schema de rutare *.stock.# se potrivește cu cheile de rutare usd.stock și eur.stock.db, dar nu stock.nasdaq.
O schemă sugerată pentru Topic Exchange este de a păstra un set de toate cheile de rutare cunoscute și de a-l actualiza atunci când editorii folosesc noi chei de rutare. Puteți defini toate legăturile pentru o anumită cheie de rutare și, astfel, puteți găsi rapid cozile de mesaje pentru un mesaj. Acest tip de schimb este opțional.
Serverul trebuie să implementeze tipul de schimb de subiecte, caz în care serverul trebuie să declare mai întâi cel puțin un schimb de subiecte denumit amq.topic în fiecare gazdă virtuală.
Tipul de schimb de anteturitipul de schimb de anteturi funcționează astfel:
Algoritmul de potrivire este controlat de un argument de legare special transmis ca o pereche nume-valoare în tabelul de argumente. Acest argument se numește „X-match”. Poate lua una dintre cele două valori, dictând modul în care alte perechi de valori de nume din tabel sunt gestionate în timpul potrivirii:
Un câmp din argumentele de legare se potrivește cu un câmp din mesaj dacă următoarea condiție este adevărată: fie câmpul din argumentele de legare nu are valoare și câmpul cu același nume este prezent în antetele mesajului, fie dacă câmpul din legarea arguments are o valoare și câmpul cu același nume există în mesajele antete și are același sens.
Orice câmp care începe cu „x -”, altul decât „X-match” este rezervat pentru utilizare ulterioară și va fi ignorat
Serverul TREBUIE să implementeze tipul de schimb de anteturi, iar serverul TREBUIE să predeclare cel puțin un tip de schimb de anteturi numit amq.match în fiecare gazdă virtuală.
Tipul de schimb de sistemTipul de schimb de sistem funcționează astfel:
Servicii de sistem care încep cu „amq”. rezervat AMQP. Toate celelalte nume pot fi folosite. Acest tip de schimb este opțional.
Tipuri de schimb personalizateToate numele de tip de schimb personalizat trebuie să înceapă cu „x -”. Tipurile de schimb care nu încep cu „x -” sunt rezervate pentru utilizare ulterioară în standardul AMQP.
Cozi de mesajeO coadă de mesaje este un FIFO numit care conține mesaje de la aplicații. Aplicațiile sunt libere să creeze, să partajeze, să utilizeze și să distrugă cozile de mesaje aflate sub autoritatea lor.
Rețineți că atunci când există mai mulți cititori dintr-o coadă, sau tranzacții cu clientul, sau utilizând câmpuri prioritare, sau utilizând selectoare de mesaje sau optimizarea livrării specifice implementării, coada poate să nu aibă caracteristici FIFO adevărate. Singura modalitate de a garanta FIFO este să aveți un singur consumator conectat la coadă. În aceste cazuri, coada poate fi descrisă ca „fifo slab”.
Cozile de mesaje pot fi permanente, temporare sau șterse automat. Cozile de mesaje persistente există până când sunt eliminate. Cozile temporare de mesaje există până când serverul este oprit. Cozile de ștergere automată durează până când nu mai sunt utilizate.
Cozile de mesaje își stochează mesajele în memorie, pe disc sau o combinație a celor două. Cozile de mesaje sunt denumite pe baza gazdei virtuale.
Cozile de mesaje conțin mesaje și le distribuie unuia sau mai multor clienți consumatori. Un mesaj trimis la o coadă de mesaje nu este niciodată trimis la mai mult de un client decât dacă a fost respins
O singură coadă de mesaje poate conține diferite tipuri de conținut simultan și independent. Adică, dacă conținutul principal și conținutul fișierului sunt trimise la aceeași coadă de mesaje, acestea vor fi livrate aplicațiilor consumatoare în mod independent, la cerere.
LegăturiLegarea este conexiunea dintre coada de mesaje și schimbul de date. Legarea definește argumentele de rutare care spun schimbului ce mesaje ar trebui să primească coada. Aplicațiile creează și distrug legături după cum este necesar pentru a direcționa fluxul de mesaje către cozile lor de mesaje. Durata de viață a unei legături depinde de cozile de mesaje pentru care sunt definite - atunci când o coadă de mesaje este distrusă, legarea ei este, de asemenea, distrusă. Semantica specifică a metodei Queue.Bind depinde de tipul de schimb.
Consumatori - consumatoriFolosim termenul consumator pentru a ne referi atât la aplicația client, cât și la entitatea care controlează modul în care o anumită aplicație client primește mesaje din coada de mesaje. Când un client „pornește un consumator”, creează o entitate consumator pe server. Când un client „anulează un consumator”, acesta distruge entitatea consumatorului de pe server. Consumatorii aparțin aceluiași canal de client și forțează coada de mesaje să trimită mesaje clientului în mod asincron.
Calitatea serviciuluiCalitatea serviciului determină viteza cu care sunt trimise mesajele. Calitatea serviciului depinde de tipul de conținut distribuit. În general, QoS folosește conceptul de „prefetch” pentru a indica câte mesaje sau câți octeți de date vor fi trimiși înainte ca clientul să confirme mesajul. Scopul este de a trimite datele mesajului în avans pentru a reduce latența.
MulțumiriO confirmare este un semnal formal de la o aplicație client către coada de mesaje că a procesat cu succes un mesaj. Există două modele posibile de validare:
Straturile de clienți pot implementa ei înșiși confirmări explicite în diferite moduri, cum ar fi imediat după primirea unui mesaj sau când o aplicație indică că l-a procesat. Aceste diferențe nu afectează AMQP sau interoperabilitatea.
controlul fluxuluiControlul fluxului este o procedură de urgență utilizată pentru a opri fluxul de mesaje de la un egal. Funcționează în același mod între client și server și este implementat de comanda Channel.Flow. Controlul fluxului este singurul mecanism care poate opri un editor supraproductiv. Un Consumator poate folosi un mecanism de preluare a ferestrei mai elegant dacă folosește Confirmări (ceea ce înseamnă de obicei utilizarea tranzacțiilor).
Convenția de denumireAceste convenții guvernează denumirea entităților AMQP. Serverul și clientul trebuie să respecte aceste convenții:
Metodele AMQP pot defini valori minime (cum ar fi numărul de consumatori dintr-o coadă de mesaje) din motive de compatibilitate. Aceste minime sunt definite în descrierea fiecărei clase.
Implementările AMQP conforme ar trebui să implementeze valori destul de generoase pentru astfel de câmpuri, minimele sunt menite să fie folosite doar pe platformele cele mai puțin capabile.
Gramaticile folosesc această notație:
Definim metodele ca:
Numărul standard de port AMQP a fost atribuit de IANA ca 5672 atât pentru TCP, cât și pentru UDP. Port UDP rezervat pentru utilizare în viitoare implementări multicast
Oferim o gramatică completă pentru AMQP (aceasta este furnizată pentru referință și s-ar putea să fiți mai interesat să urmăriți secțiunile care detaliază diferitele tipuri de cadre și formatele acestora):
amqp = protocol - antet * amqp - unitate protocol - antet = literal - protocol AMQP - protocol id - versiune literal - AMQP = % d65 .77.81.80 ; „AMQP” protocol - id = % d0 ; Trebuie să fie 0 protocol - versiune = % d0.9.1 ; _ 0-9-1 metoda = metoda - cadru [ continut ] metoda - cadru = % d1 cadru - metoda proprietati - cadru de sarcina utila - final cadru - proprietăți = sarcina utilă a canalului - dimensiune canal = short - uint ; non - zero sarcină utilă - dimensiune = lung - uint metoda - sarcina utilă = clasă - metoda id - id * amqp - câmp clasa - id = % x00 .01 - % xFF . FF metoda - id = % x00 .01 - % xFF . FF amqp - câmp = BIT / OCTET / scurt - uint / lung - uint / lung - lung - uint / scurt - șir / lung - șir / marca temporală / câmp - tabel scurt - uint = 2 * OCTET lung - uint = 4 * OCTET long - long - uint = 8 * OCTET short - string = OCTET * string - char ; lungime + continut șir - char = % x01 .. % xFF long - string = long - uint * OCTET ; lungime + continut timestamp = long - long - uint ; 64 - biți POSIX câmp - tabel = lung - uint * câmp - valoare - pereche câmp - valoare - pereche = câmp - nume câmp - valoare câmp - nume = scurt - șir câmp - valoare = 't' boolean / 'b' scurt - scurt - int / 'B' scurt - scurt - uint / 'U' scurt - int / 'u' scurt - uint / 'I' lung - int / 'i' lung - uint / 'L' lung - lung - int / 'l' long - long - uint / 'f' plutesc / 'd' dublu / „D” zecimală - valoare / 's' scurt - șir / „S” lung - șir / Câmp „A” - matrice / Marca temporală „T”. / Câmp „F” - tabel / 'V' ; nici un câmp boolean = OCTET ; 0 = FALS , altfel ADEVĂRAT scurt - scurt - int = OCTET scurt - scurt - uint = OCTET scurt - int = 2 * OCTET lung - int = 4 * OCTET lung - lung - int = 8 * OCTET float = 4 * OCTET ; IEEE -754 dublu = 8 * OCTET ; rfc1832 XDR dublu zecimală - valoare = scară lungă - uint scara = OCTET ; numărul de cifre zecimale field - array = long - int * field - value ; matrice de valori cadru - sfârşit = % xCE continut = % d2 continut - antet * continut - corp conținut - antet = cadru - antet proprietăți - cadru de sarcină utilă - final antet - sarcină utilă = conținut - conținut de clasă - conținut de greutate - corp - dimensiune proprietate - steaguri proprietate - listă continut - clasa = octet continut - greutate = % x00 continut - corp - marime = lung - lung - uint proprietate - steaguri = 15 * BIT % b0 / 15 * BIT % b1 proprietate - steaguri proprietate - listă = * amqp - câmp conținut - corp = % d3 cadru - proprietăți corp - cadru de sarcină utilă - sfârșit corp - sarcină utilă = * OCTET bataia inimii = % d8 % d0 % d0 frame - final
Folosim sintaxa BNF extinsă definită în IETF RFC 2234. În concluzie:
Clientul trebuie să înceapă o nouă conexiune prin trimiterea antetului protocolului. Aceasta este o secvență de 8 octeți:
+---+---+---+---+---+---+---+---+ | „A” | „M” | „Q” | „P” | 0 | 0 | 9 | 1 | +---+---+---+---+---+---+---+---+ 8 octețiAntetul protocolului este format din literele mari „AMQP” urmate de constanta %d0 urmată de:
Modelul de negociere a protocolului este compatibil cu protocoalele existente precum HTTP, care inițiază o conexiune cu un șir de text constant și cu firewall-uri, care urmăresc începutul unui protocol pentru a decide ce reguli să i se aplice.
Clientul și serverul negociază protocolul și versiunea după cum urmează:
Exemplu:
Clientul trimite : Serverul răspunde : AMQP % d0 .0.9.1 Conexiune . metoda de pornire AMQP % d0 .1.0.0 AMQP % d0 .0.9.1 < Închidere conexiune > HTTP AMQP % d0.0.9.1 < Închidere conexiune > _Principii de implementare a protocolului:
Toate cadrele încep cu un antet de 7 octeți constând dintr-un câmp de tip (octet), un câmp de canal (întreg scurt) și un câmp de lungime (întreg lung):
0 1 3 7 mărime + 7 mărime + 8 +------+---------+---------+ +-------------+ +------ -----+ | tip | canal | dimensiune | | sarcină utilă | | cadru - capăt | +------+---------+---------+ +-------------+ +------ -----+ octet scurt lung „ dimensiune ” octet octetAMQP definește următoarele tipuri de cadre:
Numărul canalului este 0 pentru toate cadrele care sunt globale pentru conexiune și 1-65535 pentru cadrele care se referă la anumite canale.
Câmpul de dimensiune este dimensiunea încărcăturii utile, excluzând octetul de sfârșit de cadru. În timp ce AMQP presupune un protocol conectat de încredere, folosim sfârșitul cadrului pentru a detecta erorile de încadrare cauzate de implementări incorecte de client sau server.
Principii de implementare a protocolului:
Corpurile de cadre ale metodei constau dintr-o listă invariantă de câmpuri de date numite „argumente”. Toate corpurile metodei încep cu identificatori de clasă și metodă:
0 2 4 +----------+-----------+-------------- - - | clasa - id | metoda - id | argumente ... +----------+-----------+-------------- - - scurt scurt ...Principii de implementare a protocolului:
AMQP are două niveluri de specificare a câmpurilor de date: câmpuri de date native utilizate pentru argumentele metodei și câmpuri de date transmise între aplicații în tabelele de câmp. Tabelele de câmpuri conțin un set superscript de câmpuri de date native.
AMQP definește următoarele tipuri de numere întregi native:
Numerele întregi și lungimile șirurilor sunt întotdeauna nesemnate și stocate în ordinea octeților de rețea. Nu încercăm să optimizăm cazul în care două sisteme low-high (de exemplu două procesoare Intel) vorbesc între ele.
Principii de implementare a protocolului:
AMQP își definește propriul tip de câmp de biți. Biții se acumulează în octeți întregi. Când doi sau mai mulți biți se ating într-un cadru, aceștia vor fi împachetati în unul sau mai mulți octeți, începând cu bitul cel mai mic din fiecare octet. Nu există nicio cerință ca toate valorile biților dintr-un cadru să fie învecinate, dar acest lucru se face de obicei pentru a minimiza dimensiunile cadrului.
ȘiruriȘirurile AMQP au lungime variabilă și sunt reprezentate de o lungime întreagă urmată de zero sau mai mulți octeți de date. AMQP definește două tipuri de rânduri native:
Marcajele temporale sunt stocate în formatul POSIX time_t pe 64 de biți cu o precizie de o secundă. Folosind 64 de biți, evităm problemele viitoare de împachetare asociate cu valorile time_t de 31 de biți și 32 de biți.
Marginile tabeluluiCâmpurile de tabel sunt șiruri lungi care conțin perechi nume-valoare împachetate. Perechile de valori de nume sunt codificate ca un șir scurt care specifică numele și un octet care specifică tipul valorii, urmat de valoarea în sine. Tipurile de câmpuri valide pentru tabele sunt extensii ale tipurilor native întreg, bit, șir și timestamp și sunt afișate în gramatică. Câmpurile întregi cu octeți multipli sunt întotdeauna stocate în ordinea octeților de rețea.
Principii de implementare a protocolului:
Anumite metode specifice (Publish, Deliver etc.) procesează conținutul. Vă rugăm să consultați capitolul „Specificații funcționale” pentru specificațiile fiecărei metode. Metodele care procesează conținutul fac acest lucru necondiționat.
Conținutul constă dintr-o listă de 1 sau mai multe cadre, după cum urmează:
Cadrele de conținut de pe un anumit canal sunt strict secvențiale. Adică pot fi amestecate cu cadre pentru alte canale, dar nu se pot amesteca două cadre de conținut de pe același canal și nu se pot „suprapune” unul pe altul, iar cadrele de conținut pentru același conținut nu pot fi amestecate cu cadre de metodă pe același canal . . (orig. Cadrele de conținut de pe un anumit canal sunt strict secvențiale. Adică pot fi amestecate cu cadre pentru alte canale, dar nu pot fi amestecate sau suprapuse două cadre de conținut de pe același canal și nici cadrele de conținut pentru un singur conținut nu pot fi amestecat cu cadre de metodă pe același canal.)
Rețineți că orice cadru non-conținut marchează în mod explicit sfârșitul conținutului. În timp ce dimensiunea conținutului este bine cunoscută din antetul conținutului (și, prin urmare, numărul de cadre de conținut), acest lucru permite expeditorului să abandoneze conținutul fără a fi nevoit să închidă canalul.
Principii de implementare a protocolului:
Antetul încărcăturii utile de conținut are următorul format:
0 2 4 12 14 +----------+--------+-----------+----------------+ ------------- - - | clasa - id | greutate | dimensiunea corpului | steaguri de proprietate | lista de proprietati ... +----------+--------+-----------+----------------+ ------------- - - scurt scurt lung lung scurt rest ...Principii de implementare a protocolului:
Sarcina utilă a corpului de conținut este un bloc binar „opac” care se termină cu un octet de sfârșit de cadru:
+----------------------+ +-----------+ | Sarcină utilă binară opac | | cadru - capăt | +----------------------+ +-----------+Corpul de conținut poate fi împărțit în câte cadre este necesar. Dimensiunea maximă a sarcinii utile a cadrului este negociată de ambii colegi în timpul negocierii conexiunii.
Principii de implementare a protocolului:
Cadrele Heartbeat îi spun destinatarului că expeditorul este încă în viață. Frecvența și sincronizarea cadrelor Heartbeat sunt negociate în timpul configurării conexiunii.
Principii de implementare a protocolului:
AMQP permite colegilor să creeze mai multe fluxuri independente de control. Fiecare canal acționează ca o conexiune virtuală care împarte un socket:
rame rame rame rame +-----------+-----------+-----------+-----------+ | canal | canal | canal | canal | +-----------+-----------+-----------+-----------+ | priză | +------------------------------------------------- ----+Principii de implementare a protocolului:
Serverul trebuie să se asigure că observațiile clientului privind starea serverului sunt consecvente.
Următorul exemplu arată ce înseamnă observarea clientului în acest context:
Garanția de vizibilitate asigură că clientul 2 vede coada
Serverul va considera canalul închis dacă se întâmplă oricare dintre următoarele:
Când serverul închide un canal, toate mesajele neconfirmate de pe canal sunt marcate pentru relivrare. Când serverul închide o conexiune, elimină toate entitățile șterse automat care aparțin acelei conexiuni.
În unele cazuri, metodele sincrone cerere-răspuns afectează livrarea asincronă a conținutului pe același canal, inclusiv:
Principii de implementare a protocolului:
Ordinea în care metodele trec printr-un canal este stabilă: metodele sunt primite în aceeași ordine în care sunt trimise. La nivelul de transport, acest lucru este asigurat de protocolul TCP/IP. În plus, conținutul este procesat stabil de server. În special, conținutul care urmează aceeași cale în cadrul serverului va rămâne ordonat. Pentru conținutul cu o anumită prioritate care trece printr-o singură cale, definim calea de procesare a conținutului ca fiind formată dintr-un canal de intrare, un schimb, o coadă și un canal de ieșire.
Principii de implementare a protocolului:
Folosind modelul standard de programare „excepții”, AMQP nu semnalează succes, ci doar eșec. AMQP definește două niveluri de excluderi:
Documentăm în mod oficial afirmațiile în definiția fiecărei clase și metode.
Format cod de răspunsCodurile de răspuns AMQP urmează definiția „Severități și teorie a codului de răspuns” din IETF RFC 2821.