Guida al protocollo AMQP 1.0 nel bus di servizio e in Hub eventi di Azure

AMQP (Advanced Message Queueing Protocol) 1.0 è un protocollo di frame e di trasferimento che consente di trasferire messaggi tra due parti in modo asincrono, sicuro e affidabile. È il protocollo principale di bus di servizio di Azure Messaggistica e Hub eventi di Azure.

AMQP 1.0 è il risultato di un'ampia collaborazione a livello di settore, tra fornitori di middleware, ad esempio Microsoft e Red Hat, e molti utenti di middleware di messaggistica, ad esempio JP Morgan Chase, che rappresenta il settore di servizi finanziari. Il forum di standardizzazione tecnica per il protocollo EMQP e le specifiche di estensione è OASIS e ha ottenuto l'approvazione formale come standard internazionale come ISO/IEC 19494:2014.

Obiettivi

Questo articolo riepiloga i concetti di base della specifica di messaggistica AMQP 1.0 insieme alle specifiche di estensione sviluppate dal comitato tecnico OASIS AMQP e spiega come bus di servizio di Azure implementa e si basa su queste specifiche.

L'obiettivo consiste nel permettere a tutti gli sviluppatori che usano uno stack client AMQP 1.0 esistente su qualsiasi piattaforma di interagire con il bus di servizio di Azure tramite AMQP 1.0.

Stack AMQP 1.0 comuni per utilizzo generico, ad esempio Apache Qpid Proton o AMQP.NET Lite, implementano tutti gli elementi di protocollo AMQP 1.0 principali, ad esempio sessioni o collegamenti. Questi elementi fondamentali vengono talvolta racchiusi in un'API di livello superiore; Apache Proton offre anche due, l'API Messenger imperativa e l'API Reactor reattiva.

Nella discussione seguente si presuppone che la gestione delle connessioni, delle sessioni e dei collegamenti AMQP e la gestione dei trasferimenti di fotogrammi e del controllo del flusso siano gestite dal rispettivo stack (ad esempio Apache Proton-C) e che non richiedano molta attenzione agli sviluppatori di applicazioni. Si presuppone in modo astratto l'esistenza di alcune interfacce API primitive, ad esempio la possibilità di connettersi e di creare qualche genere di oggetti di astrazione sender e receiver, che includono rispettivamente un tipo di operazione send() e receive().

Durante l'analisi delle funzionalità avanzate del bus di servizio di Azure, ad esempio l'esplorazione dei messaggi o la gestione delle sessioni, questi aspetti vengono illustrati dal punto di vista di AMQP, ma anche dal punto di vista della pseudo-implementazione a più livelli sopra questa presupposta astrazione di API.

Informazioni su AMQP

AMQP è un protocollo di frame e di trasferimento. Un protocollo di frame fornisce una struttura per i flussi di dati in ogni direzione di una connessione di rete. La struttura fornisce una descrizione di blocchi distinti di dati, detti frame, da scambiare tra le parti connesse. Le funzionalità di trasferimento assicurano che entrambe le parti possano raggiungere un accordo condiviso in merito al momento in cui i frame devono essere trasferiti e al momento in cui i trasferimenti devono essere considerati completi.

A differenza delle versioni bozza scadute precedenti del gruppo di lavoro AMQP ancora in uso da alcuni broker di messaggi, il gruppo di lavoro finale e il protocollo AMQP 1.0 standardizzato non prevede la presenza di un broker di messaggi o di una particolare topologia per le entità all'interno di un broker di messaggi.

Il protocollo può essere usato per la comunicazione peer-to-peer simmetrica con broker messaggi che supportano le query e pubblicano/sottoscrivono entità, analogamente al bus di servizio di Azure. Può essere usato anche per l'interazione con l'infrastruttura di messaggistica, in cui i modelli di interazione sono diversi rispetto alle code regolari, come nel caso di Hub eventi di Azure. Un hub eventi agisce come una coda quando gli eventi vengono inviati, ma agisce più come un servizio di archiviazione seriale quando gli eventi vengono letti da esso; assomiglia a un'unità nastro. Il client sceglie un offset nel flusso di dati disponibile e riceve quindi tutti gli eventi da tale offset, fino all'evento più recente disponibile.

Il protocollo AMQP 1.0 è progettato in modo da essere estensibile e da supportare specifiche aggiuntive che ne migliorano le funzionalità. Questo aspetto è illustrato dalle tre specifiche di estensioni incluse in questo argomento. Per la comunicazione tramite l'infrastruttura HTTPS/WebSocket esistente, la configurazione delle porte TCP AMQP native potrebbe essere difficile. Una specifica di associazione definisce come suddividere in livelli AMQP su WebSockets. Per l'interazione con l'infrastruttura di messaggistica in una modalità di richiesta/risposta per finalità di gestione o per offrire funzionalità avanzate, la specifica relativa alla gestione di AMQP definisce le primitive di interazione di base necessarie. Per l'integrazione con il modello di autorizzazione federata, la specifica relativa alla sicurezza basata su attestazioni di AMQP definisce il modo in cui associare e rinnovare i token di autorizzazione associati ai collegamenti.

Scenari AMQP di base

Questa sezione illustra l'uso di base di AMQP 1.0 con il bus di servizio di Azure, che include la creazione di connessioni, sessioni e collegamenti e il trasferimento di messaggi a e da entità del bus di servizio quali code, argomenti e sottoscrizioni.

L'origine più autorevole per informazioni sul funzionamento di AMQP è la specifica AMQP 1.0, ma la specifica è stata scritta per guidare con precisione l'implementazione e non per insegnare il protocollo. Questa sezione è incentrata sull'introduzione della terminologia necessaria per descrivere l'uso di AMQP 1.0 da parte del bus di servizio. Per un'introduzione più completa ad AMQP e per una discussione più ampia su AMQP 1.0, vedere questa esercitazione video.

Connessioni e sessioni

AMQP definisce contenitori i programmi di comunicazione. I contenitori includono nodi, ovvero entità che comunicano all'interno di tali contenitori. Una coda può essere un nodo di questo tipo. AMQP consente il multiplexing, quindi una singola connessione può essere usata per molti percorsi di comunicazione tra i nodi. Un client applicazione, ad esempio, può ricevere contemporaneamente da una coda e inviare a un'altra coda sulla stessa connessione di rete.

Diagram showing Sessions and Connections between containers.

La connessione di rete viene quindi ancorata al contenitore. Viene avviato dal contenitore nel ruolo client che effettua una connessione socket TCP in uscita a un contenitore nel ruolo ricevitore, che rimane in ascolto e accetta connessioni TCP in ingresso. L'handshake di connessione include la negoziazione della versione del protocollo, la dichiarazione o la negoziazione dell'uso di TLS/SSL (Transport Level Security) e un handshake di autenticazione/autorizzazione nell'ambito di connessione basato su SASL.

bus di servizio di Azure o Hub eventi di Azure richiede sempre l'uso di TLS. Supporta connessioni sulla porta TCP 5671. La connessione TCP viene prima di tutto sovrapposta a TLS prima dell'handshake del protocollo AMQP e supporta anche connessioni sulla porta TCP 5672, in cui il server offre immediatamente un aggiornamento obbligatorio della connessione a TLS usando il modello prescritto da AMQP. L'associazione a WebSocket AMQP crea un tunnel sulla porta TCP 443 che è quindi equivalente a connessioni di tipo AMQP 5671.

Dopo la configurazione della connessione e di TLS, il bus di servizio offre due opzioni per il meccanismo SASL:

  • L'opzione SASL PLAIN viene usata solitamente per passare credenziali di tipo nome utente e password a un server. bus di servizio non dispone di account, ma denominati Regole di sicurezza dell'accesso condiviso, che conferisce i diritti e sono associate a una chiave. Il nome di una regola viene usato come nome utente e la chiave, sotto forma di testo con codifica Base64, viene usata come password. I diritti associati alla regola scelta determinano le operazioni consentite sulla connessione.
  • L'opzione SASL ANONYMOUS viene usata per ignorare l'autorizzazione SASL quando il client vuole usare il modello di sicurezza basato sulle attestazioni che viene illustrato più avanti. Questa opzione consente di stabilire una connessione client in modo anonimo per un breve periodo di tempo, durante il quale il client può interagire solo con l'endpoint CBS e l'handshake CBS deve essere completato.

Dopo aver stabilito la connessione di trasporto, i contenitori dichiarano le dimensioni massime del frame che sono disposti a gestire e dopo un timeout di inattività si disconnetteranno unilateralmente se non è presente alcuna attività nella connessione.

I contenitori dichiarano anche il numero di canali simultanei supportati. Un canale è un percorso di trasferimento virtuale, unidirezionale e in uscita sulla connessione. Una sessione accetta un canale da ogni contenitore interconnesso per formare un percorso di comunicazione bidirezionale.

Le sessioni hanno un modello di controllo del flusso basato su finestra; quando viene creata una sessione, ogni parte dichiara il numero di fotogrammi che è disposto ad accettare nella finestra di ricezione. Quando le parti si scambiano i frame, i frame trasferiti completano tale finestra e i trasferimenti si arrestano quando la finestra è completa e fino a quando la finestra non viene reimpostata o espansa usando la performativa di flusso. Performativa è il termine di AMQP per i gesti a livello di protocollo scambiati tra le due parti.

Il modello basato su finestra è quasi equivalente al concetto TCP del controllo di flusso basato su finestra, ma a livello di sessione all'interno del socket. Il concetto del protocollo che permette più sessioni contemporanee consente di dare la precedenza al traffico a priorità elevata rispetto al traffico normale con limitazioni.

Il bus di servizio di Azure usa attualmente esattamente una sessione per ogni connessione. La dimensione massima del fotogramma bus di servizio è di 262.144 byte (256 K byte) per bus di servizio Standard. È 1048576 (100 MB) per bus di servizio Premium e Hub eventi. bus di servizio non impone finestre di limitazione specifiche a livello di sessione, ma reimposta regolarmente la finestra come parte del controllo del flusso a livello di collegamento (vedere la sezione successiva).

Le connessioni, le sessioni e i canali sono temporanei. In caso di interruzione della connessione sottostante, è necessario ristabilire le connessioni, il tunnel TLS, il contesto di autorizzazione SASL e le sessioni.

Requisiti delle porte in uscita AMQP

I client che usano connessioni AMQP su TCP richiedono l'apertura delle porte 5671 e 5672 nel firewall locale. Oltre a queste porte, potrebbe essere necessario aprire porte aggiuntive se la funzionalità EnableLinkRedirect è abilitata. EnableLinkRedirect è una nuova funzionalità di messaggistica che consente di ignorare un hop durante la ricezione di messaggi, consentendo così di aumentare la velocità effettiva. Il client inizierà a comunicare direttamente con il servizio back-end sull'intervallo di porte 104XX, come illustrato nell'immagine seguente.

List of destination ports

Un client .NET non riesce con un'eccezione SocketException ("Tentativo di accedere a un socket in modo non consentito dalle autorizzazioni di accesso") se queste porte sono bloccate dal firewall. La funzionalità può essere disabilitata impostando EnableAmqpLinkRedirect=false nella stringa di connessione, che forza i client a comunicare con il servizio remoto sulla porta 5671.

L'associazione WebSocket AMQP fornisce un meccanismo per il tunneling di una connessione AMQP tramite un trasporto WebSocket. Questa associazione crea un tunnel sulla porta TCP 443, equivalente alle connessioni AMQP 5671. Usare WebSocket AMQP se si è dietro un firewall che blocca le connessioni TCP sulle porte 5671, 5672, ma consente connessioni TCP sulla porta 443 (https).

AMQP trasferisce i messaggi sui collegamenti. Un collegamento è un percorso di comunicazione creato su una sessione che consente il trasferimento di messaggi in una direzione. La negoziazione dello stato del trasferimento viene effettuata sul collegamento ed è bidirezionale tra le parti connesse.

Screenshot showing a Session carrying a link connection between two containers.

I collegamenti possono essere creati da uno dei contenitori in qualsiasi momento e su una sessione esistente. Ciò rende il protocollo AMQP diverso da molti altri protocolli, inclusi HTTP e MQTT, dove l'inizializzazione dei trasferimenti e il percorso di trasferimento sono un privilegio esclusivo della parte che crea la connessione socket.

Il contenitore che inizializza il collegamento chiede al contenitore opposto di accettare un collegamento e sceglie un ruolo di mittente o ricevitore. Ogni contenitore può quindi inizializzare la creazione di percorsi di comunicazione unidirezionali o bidirezionali. I percorsi bidirezionali vengono modellati come coppie di collegamenti.

I collegamenti sono denominati e sono associati ai nodi. Come indicato all'inizio, i nodi sono entità comunicanti all'interno di un contenitore.

Nel bus di servizio un nodo corrisponde direttamente a una coda, un argomento, una sottoscrizione o una coda secondaria di messaggi non recapitabili di una coda o una sottoscrizione. Il nome del nodo usato in AMQP è quindi il nome relativo dell'entità all'interno dello spazio dei nomi del bus di servizio. Se una coda è denominata myqueue, tale nome corrisponde anche al nome del nodo AMQP. Una sottoscrizione dell'argomento segue la convenzione dell'interfaccia API HTTP e viene ordinata in una raccolta di risorse "subscriptions". Pertanto una sottoscrizione sub su un argomento mytopic ha il nome di nodo AMQP mytopic/subscriptions/sub.

Il client di connessione è necessario anche per usare un nome di nodo locale per la creazione di collegamenti; bus di servizio non è prescrittivo sui nomi dei nodi e non li interpreta. Gli stack del client AMQP 1.0 usano in genere uno schema per assicurare che i nomi di nodi temporanei siano univoci nell'ambito del client.

Trasferimenti

Dopo la creazione di un collegamento, è possibile trasferire i messaggi su tale collegamento. In AMQP un trasferimento viene eseguito con un gesto esplicito del protocollo, ovvero la performativa transfer, che sposta un messaggio dal mittente al ricevitore tramite un collegamento. Un trasferimento è completo quando è "finalizzato", ovvero quando entrambe le parti hanno raggiunto un accordo condiviso dell'esito del trasferimento.

A diagram showing a message's transfer between the Sender and Receiver and disposition that results from it.

Nel caso più semplice, il mittente può scegliere di inviare messaggi "pre-risolti", ovvero che il client non è interessato al risultato e il destinatario non fornisce alcun feedback sul risultato dell'operazione. Questa modalità è supportata dal bus di servizio a livello del protocollo AMQP, ma non viene esposta in nessuna API client.

La situazione normale prevede l'invio di messaggi non finalizzati. Il ricevitore indica quindi l'accettazione o il rifiuto usando la performativa disposition. Il rifiuto si verifica quando il ricevitore non può accettare il messaggio per qualsiasi motivo e il messaggio di rifiuto contiene informazioni sul motivo, ovvero una struttura di errore definita da AMQP. Se i messaggi vengono rifiutati a causa di errori interni all'interno di bus di servizio, il servizio restituisce informazioni aggiuntive all'interno di tale struttura che può essere usata per fornire suggerimenti diagnostici al personale di supporto se si stanno inviando richieste di supporto. Informazioni dettagliate sugli errori sono disponibili più avanti.

Una forma speciale di rifiuto è costituita dallo stato released, che indica che il ricevitore non ha alcuna obiezione tecnica al trasferimento, ma non è al tempo stesso interessato alla finalizzazione del trasferimento. Questo caso esiste, ad esempio, quando un messaggio viene recapitato a un client bus di servizio e il client sceglie di "abbandonare" il messaggio perché non è in grado di eseguire il lavoro risultante dall'elaborazione del messaggio; il recapito del messaggio stesso non è in errore. Una variazione di questo stato è lo stato modified, che consente modifiche al messaggio durante il rilascio. Questo stato non viene attualmente usato da bus di servizio.

La specifica AMQP 1.0 definisce uno stato di disposizione aggiuntivo denominato received, che semplifica in modo specifico la gestione del recupero del collegamento. Il recupero del collegamento consente di ricostituire lo stato di un collegamento e di eventuali recapiti in sospeso su una nuova connessione e sessione, in caso di perdita della connessione e della sessione precedente.

bus di servizio non supporta il ripristino dei collegamenti. Se il client perde la connessione a bus di servizio con un trasferimento di messaggi in sospeso, il trasferimento dei messaggi viene perso e il client deve riconnettersi, ristabilire il collegamento e ritentare il trasferimento.

Di conseguenza, bus di servizio e Hub eventi supportano il trasferimento "almeno una volta" in cui il mittente può essere assicurato per il messaggio che è stato archiviato e accettato, ma non supporta i trasferimenti "esattamente una volta" a livello AMQP, in cui il sistema tenterebbe di recuperare il collegamento e continuare a negoziare lo stato di recapito per evitare la duplicazione del trasferimento del messaggio.

Per compensare possibili invii duplicati, il bus di servizio supporta il rilevamento dei duplicati come funzionalità facoltativa nelle code e negli argomenti. Il rilevamento dei duplicati registra gli ID di tutti i messaggi in arrivo durante un intervallo di tempo definito dall'utente, quindi rilascia automaticamente tutti i messaggi inviati con gli stessi ID di messaggio durante lo stesso intervallo.

Controllo del flusso

Oltre al modello di controllo di flusso a livello di sessione illustrato in precedenza, ogni collegamento ha il proprio modello di controllo di flusso. Il controllo di flusso a livello di sessione protegge il contenitore dalla necessità di gestire un numero eccessivo di frame alla volta. Il controllo di flusso a livello di collegamento assegna all'applicazione la responsabilità di specificare il numero di messaggi da gestire da un collegamento e del momento in cui gestirli.

Screenshot of a log showing Source, Destination, Source Port, Destination Port, and Protocol Name. In the first row the Destination Port 10401 (0x28 A 1) is outlined in black.

I trasferimenti su un collegamento possono essere eseguiti solo quando il mittente dispone di credito di collegamento sufficiente. Il credito di collegamento è un contatore impostato dal ricevitore usando la performativa flow, che ha come ambito un collegamento. Quando riceve credito di collegamento, il mittente prova a usare completamente tale credito recapitando messaggi. Ogni recapito di messaggi riduce di 1 il credito di collegamento rimanente. Quando il credito di collegamento viene usato completamente, i recapiti si interrompono.

Quando il bus di servizio ha il ruolo di ricevitore, fornisce immediatamente al mittente un credito di collegamento molto ampio per consentire l'invio immediato dei messaggi. Durante l'uso del credito di collegamento, il bus di servizio invia di tanto in tanto una performativa flow al mittente per aggiornare il saldo del credito di collegamento.

Il bus di servizio con ruolo di mittente invia i messaggi in modo da usare completamente un eventuale credito di collegamento residuo.

Una chiamata di "ricezione" a livello di API viene convertita in una performativa flow inviata al bus di servizio dal client e il bus di servizio usa tale credito selezionando il primo messaggio non bloccato disponibile dalla coda, bloccandolo e trasferendolo. Se non è disponibile alcun messaggio per il recapito, qualsiasi credito in sospeso stabilito con tale entità rimane registrato in ordine di arrivo e i messaggi vengono bloccati e trasferiti quando diventano disponibili, per usare qualsiasi credito in sospeso.

Il blocco di un messaggio viene rilasciato quando il trasferimento viene finalizzato in uno degli stati terminali, ovvero accepted, rejected o released. Il messaggio viene rimosso dal bus di servizio quando lo stato terminale è accepted. Il messaggio rimane nel bus di servizio e viene recapitato al ricevitore successivo quando il trasferimento raggiunge uno degli altri stati. Il bus di servizio sposta automaticamente il messaggio nella coda di messaggi non recapitabili dell'entità quando raggiunge il numero massimo di recapiti consentiti per l'entità a causa di rifiuti o rilasci ripetuti.

Anche se le API di bus di servizio non espongono direttamente tale opzione oggi, un client del protocollo AMQP di livello inferiore può usare il modello di credito di collegamento per trasformare l'interazione "pull" dell'emissione di un'unità di credito per ogni richiesta di ricezione in un modello "push-style" emettendo un numero elevato di crediti di collegamento e quindi ricevere messaggi non appena diventano disponibili senza ulteriori interazioni. Il push è supportato tramite le impostazioni della proprietà ServiceBusProcessor.PrefetchCount o ServiceBusReceiver.PrefetchCount . Quando sono diversi da zero, il client AMQP lo usa come credito di collegamento.

In questo contesto è importante notare che il clock per la scadenza del blocco sul messaggio nell'entità viene avviato quando il messaggio viene accettato dall'entità e non all'inizio della fase di trasmissione. Ogni volta che il client indica l'idoneità a ricevere messaggi emettendo il credito di collegamento, è quindi previsto che estrae attivamente i messaggi attraverso la rete e sia pronto per gestirli. In caso contrario, il blocco del messaggio potrebbe essere scaduto prima del recapito del messaggio. L'uso del controllo di flusso del credito di collegamento deve riflettere in modo diretto la capacità immediata di gestire i messaggi disponibili inviati al ricevitore.

In breve, le sezioni seguenti forniscono una panoramica schematica del flusso performativo durante diverse interazioni con le API. Ogni sezione descrive un'operazione logica diversa. Alcune di queste interazioni potrebbero essere "lazy", ovvero potrebbero essere eseguite solo quando necessario. La creazione di un mittente di messaggi potrebbe non causare un'interazione di rete finché il primo messaggio non viene inviato o richiesto.

Le frecce della seguente tabella visualizzano la direzione del flusso performativo.

Creare il ricevitore dei messaggi

Client Bus di servizio
--> attach(<br/>name={link name},<br/>handle={numeric handle},<br/>role=**receiver**,<br/>source={entity name},<br/>target={client link ID}<br/>) Il client si collega a un'entità come ricevitore
Il bus di servizio risponde collegando la propria estremità del collegamento <-- attach(<br/>name={link name},<br/>handle={numeric handle},<br/>role=**sender**,<br/>source={entity name},<br/>target={client link ID}<br/>)

Creare il mittente dei messaggi

Client Bus di servizio
--> attach(<br/>name={link name},<br/>handle={numeric handle},<br/>role=**sender**,<br/>source={client link ID},<br/>target={entity name}<br/>) Nessuna azione
Nessuna azione <-- attach(<br/>name={link name},<br/>handle={numeric handle},<br/>role=**receiver**,<br/>source={client link ID},<br/>target={entity name}<br/>)

Creare il mittente dei messaggi (errore)

Client Bus di servizio
--> attach(<br/>name={link name},<br/>handle={numeric handle},<br/>role=**sender**,<br/>source={client link ID},<br/>target={entity name}<br/>) Nessuna azione
Nessuna azione <-- attach(<br/>name={link name},<br/>handle={numeric handle},<br/>role=**receiver**,<br/>source=null,<br/>target=null<br/>)<br/><br/><-- detach(<br/>handle={numeric handle},<br/>closed=**true**,<br/>error={error info}<br/>)

Chiudere il ricevitore/mittente dei messaggi

Client Bus di servizio
--> detach(<br/>handle={numeric handle},<br/>closed=**true**<br/>) Nessuna azione
Nessuna azione <-- detach(<br/>handle={numeric handle},<br/>closed=**true**<br/>)

Inviare (operazione riuscita)

Client Bus di servizio
--> transfer(<br/>delivery-id={numeric handle},<br/>delivery-tag={binary handle},<br/>settled=**false**,,more=**false**,<br/>state=**null**,<br/>resume=**false**<br/>) Nessuna azione
Nessuna azione <-- disposition(<br/>role=receiver,<br/>first={delivery ID},<br/>last={delivery ID},<br/>settled=**true**,<br/>state=**accepted**<br/>)

Inviare (errore)

Client Bus di servizio
--> transfer(<br/>delivery-id={numeric handle},<br/>delivery-tag={binary handle},<br/>settled=**false**,,more=**false**,<br/>state=**null**,<br/>resume=**false**<br/>) Nessuna azione
Nessuna azione <-- disposition(<br/>role=receiver,<br/>first={delivery ID},<br/>last={delivery ID},<br/>settled=**true**,<br/>state=**rejected**(<br/>error={error info}<br/>)<br/>)

Ricezione

Client Bus di servizio
--> flow(<br/>link-credit=1<br/>) Nessuna azione
Nessuna azione < transfer(<br/>delivery-id={numeric handle},<br/>delivery-tag={binary handle},<br/>settled=**false**,<br/>more=**false**,<br/>state=**null**,<br/>resume=**false**<br/>)
--> disposition(<br/>role=**receiver**,<br/>first={delivery ID},<br/>last={delivery ID},<br/>settled=**true**,<br/>state=**accepted**<br/>) Nessuna azione

Ricevere più messaggi

Client Bus di servizio
--> flow(<br/>link-credit=3<br/>) Nessuna azione
Nessuna azione < transfer(<br/>delivery-id={numeric handle},<br/>delivery-tag={binary handle},<br/>settled=**false**,<br/>more=**false**,<br/>state=**null**,<br/>resume=**false**<br/>)
Nessuna azione < transfer(<br/>delivery-id={numeric handle+1},<br/>delivery-tag={binary handle},<br/>settled=**false**,<br/>more=**false**,<br/>state=**null**,<br/>resume=**false**<br/>)
Nessuna azione < transfer(<br/>delivery-id={numeric handle+2},<br/>delivery-tag={binary handle},<br/>settled=**false**,<br/>more=**false**,<br/>state=**null**,<br/>resume=**false**<br/>)
--> disposition(<br/>role=receiver,<br/>first={delivery ID},<br/>last={delivery ID+2},<br/>settled=**true**,<br/>state=**accepted**<br/>) Nessuna azione

Messaggi

Le sezioni seguenti spiegano quali proprietà delle sessioni di messaggi AMQP standard vengono usate dal bus di servizio e ne illustrano il mapping al set di API del bus di servizio.

Qualsiasi proprietà che l'applicazione deve definire deve essere mappata alla mappa di application-properties AMQP.

Nome campo Utilizzo Nome API
durable - -
priority - -
ttl Durata di questo messaggio TimeToLive
first-acquirer - -
delivery-count - DeliveryCount

proprietà

Nome campo Utilizzo Nome API
message-id Identificatore freeform definito dall'applicazione per questo messaggio. Usato per il rilevamento dei duplicati. MessageId
user-id Identificatore dell'utente definito dall'applicazione, non interpretato dal bus di servizio. Non è accessibile tramite l'API del bus di servizio.
a Identificatore della destinazione definito dall'applicazione, non interpretato dal bus di servizio. Al
subject Identificatore dello scopo del messaggio definito dall'applicazione, non interpretato dal bus di servizio. Argomento
reply-to Indicatore del percorso di risposta definito dall'applicazione, non interpretato dal bus di servizio. ReplyTo
correlation-id Identificatore della correlazione definito dall'applicazione, non interpretato dal bus di servizio. CorrelationId
content-type Indicatore del tipo di contenuto definito dall'applicazione per il corpo, non interpretato dal bus di servizio. ContentType
content-encoding Indicatore della codifica del contenuto definito dall'applicazione per il corpo, non interpretato dal bus di servizio. Non è accessibile tramite l'API del bus di servizio.
absolute-expiry-time Indica l'istante assoluto in cui scadrà il messaggio. Viene ignorato nell'input (viene osservato il valore TTL dell'intestazione), viene considerato autorevole nell'output. Non è accessibile tramite l'API del bus di servizio.
creation-time Dichiara l'ora di creazione del messaggio. Non usato dal bus di servizio Non è accessibile tramite l'API del bus di servizio.
group-id Identificatore definito dall'applicazione per un set correlato di messaggi. Usato per le sessioni del bus di servizio. SessionId
group-sequence Contatore che identifica il numero di sequenza relativo al messaggio in una sessione. Ignorato dal bus di servizio. Non è accessibile tramite l'API del bus di servizio.
reply-to-group-id - ReplyToSessionId

Annotazioni di messaggio

Esistono alcune altre proprietà del messaggio del bus di servizio, che non fanno parte delle proprietà del messaggio AMQP e vengono passate come MessageAnnotations nel messaggio.

Mappatura della chiave di annotazione Utilizzo Nome API
x-opt-scheduled-enqueue-time Dichiara in quale momento dovrà essere visualizzato il messaggio nell'entità ScheduledEnqueueTime
x-opt-partition-key Chiave definite dall'applicazione che stabilisce in quale partizione dovrà essere recapitato il messaggio. PartitionKey
x-opt-via-partition-key Valore definito dall'applicazione della chiave di partizione quando una transazione deve essere utilizzata per inviare messaggi tramite una coda di trasferimento. TransactionPartitionKey
x-opt-enqueued-time Ora UTC definita dal servizio che rappresenta l’ora effettiva di inserimento in coda del messaggio. Ignorato durante l'input. EnqueuedTime
x-opt-sequence-number Numero univoco definito dal servizio assegnato a un messaggio. SequenceNumber
x-opt-offset Numero di sequenza di accodamento definito dal servizio del messaggio. EnqueuedSequenceNumber
x-opt-locked-until Definito dal servizio. La data e l'ora fino alla quale il messaggio sarà bloccato nella coda/sottoscrizione. LockedUntil
x-opt-deadletter-source Definito dal servizio. Se il messaggio viene ricevuto dalla coda dei messaggi non recapitabili, rappresenta l'origine del messaggio originale. DeadLetterSource

Funzionalità delle transazioni

Una transazione raggruppa due o più operazioni in un ambito di esecuzione. Per natura, questo tipo di transazione deve garantire che tutte le operazioni appartenenti a un determinato gruppo di operazioni abbiano esito positivo o negativo. Le operazioni sono raggruppate per un identificatore txn-id.

Per l'interazione transazionale, il client viene usato come transaction controller che controlla le operazioni da raggruppare. Il servizio del bus di servizio di Microsoft Azure viene utilizzato come un transactional resource ed esegue operazioni come richiesto dal transaction controller.

Il client e il servizio comunicano tramite una control link che viene stabilita dal client. I declare messaggi e discharge vengono inviati dal controller tramite il collegamento di controllo per allocare e completare le transazioni rispettivamente (non rappresentano la demarcazione del lavoro transazionale). L'invio/ricezione effettivo non viene eseguito su questo collegamento. Ogni operazione transazionale richiesta viene identificata in modo esplicito con l'oggetto desiderato txn-id e pertanto può verificarsi su qualsiasi collegamento nel Connessione ion. Se il collegamento di controllo è chiuso mentre esistono transazioni non eseguite, viene immediatamente eseguito il rollback di tutte le transazioni e i tentativi di eseguire un'ulteriore attività transazionale su di esse falliranno. I messaggi sul collegamento di controllo non devono essere predefiniti.

Ogni connessione deve avviare il proprio collegamento di controllo per poter iniziare e terminare le transazioni. Il servizio definisce una destinazione speciale che funziona come un coordinator. Il client/controller stabilisce un collegamento di controllo a questa destinazione. Il collegamento di controllo è esterno ai limiti di un'entità, vale a dire che lo stesso collegamento di controllo può essere usato per avviare ed eseguire le transazioni per più entità.

Avviare una transazione

Per avviare attività transazionali. il controller deve ricevere un txn-id dal coordinatore. Ciò avviene mediante l'invio di un messaggio di tipo declare. Se la dichiarazione ha esito positivo, il coordinatore risponde con un risultato di disposizione che esegue l'oggetto assegnato txn-id.

Client (controller) Orientamento Bus di servizio (coordinatore)
attach(
name={nome collegamento},
... ,
role=sender,
target=Coordinator
)
------>
<------ attach(
name={nome collegamento},
... ,
target=Coordinator()
)
transfer(
delivery-id=0, ...)
{ ValoreAmqp (Dichiara())}
------>
<------ disposizione(
first=0, last=0,
state=Declared(
txn-id={ID transazione}
))

Eseguire una transazione

Il controller conclude l'attività transazionale inviando un discharge messaggio al coordinatore. Il controller indica che desidera eseguire il commit o il rollback dell'attività transazionale impostando il flag fail nel corpo di esecuzione. Se il coordinatore non riesce a completare l’esecuzione, il messaggio viene rifiutato con questo risultato trasportando transaction-error.

Nota: esito negativo=vero intende l’esecuzione di rollback di una transazione ed esito negativo=falso fa riferimento all’esecuzione del commit.

Client (controller) Orientamento Bus di servizio (coordinatore)
transfer(
delivery-id=0, ...)
{ ValoreAmqp (Dichiara())}
------>
<------ disposizione(
first=0, last=0,
state=Declared(
txn-id={ID transazione}
))
. . .
Attività transazionali
in altri collegamenti
. . .
transfer(
delivery-id=57, ...)
{ ValoreAmqp (
Scarica(txn-id=0,
fail=false)
)}
------>
<------ disposizione(
first=57, last=57,
state=Accepted())

Invio di un messaggio in una transazione

Tutto il lavoro transazionale viene eseguito con lo stato transactional-state di recapito transazionale che porta il txn-id. Quando si inviano messaggi, lo stato transazionale viene trasportato dal frame di trasferimento del messaggio.

Client (controller) Orientamento Bus di servizio (coordinatore)
transfer(
delivery-id=0, ...)
{ ValoreAmqp (Dichiara())}
------>
<------ disposizione(
first=0, last=0,
state=Declared(
txn-id={ID transazione}
))
transfer(
handle=1,
delivery-id=1,
state=
TransactionalState(
txn-id=0)
)
{ payload }
------>
<------ disposizione(
first=1, last=1,
state=TransactionalState(
txn-id=0,
outcome=Accepted())
)

Eliminazione di un messaggio in una transazione

L’eliminazione del messaggio include operazioni come Complete / Abandon / DeadLetter / Defer. Per eseguire queste operazioni all'interno di una transazione, trasmettere transactional-state con la disposizione.

Client (controller) Orientamento Bus di servizio (coordinatore)
transfer(
delivery-id=0, ...)
{ ValoreAmqp (Dichiara())}
------>
<------ disposizione(
first=0, last=0,
state=Declared(
txn-id={ID transazione}
))
<------ transfer(
handle=2,
delivery-id=11,
state=null)
{ payload }
disposizione(
first=11, last=11,
state=TransactionalState(
txn-id=0,
outcome=Accepted())
)
------>

Funzionalità avanzate del bus di servizio

Questa sezione illustra le funzionalità avanzate del bus di servizio di Azure basate sulle estensioni provvisorie per AMQP attualmente in fase di sviluppo nel comitato tecnico OASIS per AMQP. Il bus di servizio implementa la versione più recente di queste estensioni provvisorie e adotta le modifiche introdotte non appena le versioni provvisorie acquisiscono lo stato standard.

Nota

Le operazioni avanzate per la messaggistica del bus di servizio sono supportate tramite un modello di richiesta/risposta. I dettagli di queste operazioni sono descritti nell'articolo AMQP 1.0 in Service Bus: request/response-based operations (AMQP 1.0 nel bus di servizio: operazioni basate su richiesta/risposta).

Gestione di AMQP

La specifica di gestione di AMQP è la prima delle estensioni provvisorie illustrate in questo articolo. Questa specifica definisce un set di protocolli sovrapposti al protocollo AMQP che consente interazioni di gestione con l'infrastruttura di messaggistica tramite AMQP. La specifica definisce operazioni generiche, ad esempio create, read, update e delete per la gestione di entità all'interno di un'infrastruttura di messaggistica e di un set di operazioni di query.

Tutti questi gesti richiedono un'interazione di tipo richiesta/risposta tra il client e l'infrastruttura di messaggistica. Pertanto la specifica definisce come modellare tale schema di interazione su AMQP: il client si connette alla struttura di messaggistica, inizializza una sessione e quindi crea una coppia di collegamenti. In un collegamento il client funge da mittente e nell'altro funge da ricevitore, creando quindi una coppia di collegamenti che può fungere da canale bidirezionale.

Operazione logica Client Bus di servizio
Creare un percorso di richiesta/risposta --> attach(<br/>name={*link name*},<br/>handle={*numeric handle*},<br/>role=**sender**,<br/>source=**null**,<br/>target=”myentity/$management”<br/>) Nessuna azione
Creare un percorso di richiesta/risposta Nessuna azione \<-- attach(<br/>name={*link name*},<br/>handle={*numeric handle*},<br/>role=**receiver**,<br/>source=null,<br/>target=”myentity”<br/>)
Creare un percorso di richiesta/risposta --> attach(<br/>name={*link name*},<br/>handle={*numeric handle*},<br/>role=**receiver**,<br/>source=”myentity/$management”,<br/>target=”myclient$id”<br/>)
Creare un percorso di richiesta/risposta Nessuna azione \<-- attach(<br/>name={*link name*},<br/>handle={*numeric handle*},<br/>role=**sender**,<br/>source=”myentity”,<br/>target=”myclient$id”<br/>)

Se la coppia di collegamenti è disponibile, l'implementazione del percorso di richiesta/risposta è molto semplice: una richiesta è un messaggio inviato a un'entità all'interno dell'infrastruttura di messaggistica che comprende questo modello. Nel messaggio di richiesta il campo reply-to nella sezione properties è impostato sull'identificatore target per il collegamento in cui recapitare la risposta. L'entità di gestione elabora la richiesta e recapita la risposta sul collegamento il cui identificatore target corrisponde all'identificatore reply-to indicato.

Il modello richiede ovviamente che il contenitore client e l'identificatore generato dal client per la destinazione di risposta siano univoci in tutti i client e, per motivi di sicurezza, che siano anche difficili da prevedere.

Gli scambi di messaggi utilizzati per il protocollo di gestione e per tutti gli altri protocolli che usano lo stesso modello si verificano a livello di applicazione; non definiscono nuovi movimenti a livello di protocollo AMQP. Questo approccio è intenzionale e consente alle applicazioni di sfruttare immediatamente i vantaggi di queste estensioni con gli stack conformi ad AMQP 1.0.

bus di servizio attualmente non implementa alcuna funzionalità di base della specifica di gestione, ma il modello di richiesta/risposta definito dalla specifica di gestione è fondamentale per la funzionalità di sicurezza basata sulle attestazioni e per quasi tutte le funzionalità avanzate descritte nelle sezioni seguenti:

Autorizzazione basata sulle attestazioni

La bozza di specifica dell'autorizzazione basata sulle attestazioni (CBS, Claims-Based-Authorization specification) di AMQP si basa sul modello richiesta/risposta della specifica di gestione e illustra un modello generalizzato per l'uso dei token di sicurezza federati con AMQP.

Il modello di sicurezza predefinito di AMQP illustrato nell'introduzione è basato su SASL e si integra con l'handshake di connessione di AMQP. L'uso di SASL risulta vantaggioso perché fornisce un modello estendibile per cui è stato definito un set di meccanismi, che possono risultare utili per qualsiasi protocollo che si basa formalmente su SASL. Questi meccanismi includono ad esempio "PLAIN" per il trasferimento di nomi utente e password, "EXTERNAL" per l'associazione alla sicurezza a livello TLS, "ANONYMOUS" per indicare l'assenza di un'autenticazione/autorizzazione esplicita e una vasta gamma di meccanismi aggiuntivi che consentono il passaggio di credenziali di autenticazione e/o autorizzazione o token.

L'integrazione di AMQP con SASL presenta due svantaggi:

  • Tutte le credenziali e i token hanno come ambito la connessione. Un'infrastruttura di messaggistica potrebbe voler fornire un controllo di accesso differenziato per ogni entità; ad esempio, consentendo al bearer di un token di inviare alla coda A ma non alla coda B. Con il contesto di autorizzazione ancorato alla connessione, non è possibile usare una singola connessione e tuttavia usare token di accesso diversi per la coda A e la coda B.
  • I token di accesso sono in genere validi solo per un periodo limitato di tempo. L'utente dovrà quindi riacquisire periodicamente i token e l'autorità emittente dei token potrà rifiutare l'emissione di un nuovo token se le autorizzazioni di accesso dell'utente sono state modificate. Le connessioni AMQP potrebbero durare per lunghi periodi di tempo. Il modello SASL offre solo la possibilità di impostare un token in fase di connessione, il che significa che l'infrastruttura di messaggistica deve disconnettere il client alla scadenza del token o deve accettare il rischio di consentire la comunicazione continua con un client che potrebbe essere stato revocato nel frattempo.

La specifica CBS per AMQP, implementata dal bus di servizio, offre una soluzione ideale entrambi i problemi: consente al client di associare token di accesso a ogni nodo e di aggiornare questi token prima della scadenza, senza interrompere il flusso di messaggi.

CBS definisce un nodo di gestione virtuale, denominato $cbs, che deve essere specificato dall'infrastruttura di messaggistica. Il nodo di gestione accetta i token per conto di qualsiasi altro nodo nell'infrastruttura di messaggistica.

Il gesto del protocollo è uno scambio di tipo richiesta/risposta, in base a quanto definito dalla specifica di gestione. Ciò significa che il client stabilisce una coppia di collegamenti con il nodo $cbs, passa una richiesta al collegamento in uscita e infine attende la risposta sul collegamento in ingresso.

Ecco le proprietà dell'applicazione per il messaggio di richiesta:

Chiave Facoltativo Tipo di valore Contenuti del valore
operation No stringa put-token
type No stringa Tipo di token inserito.
name No stringa "Destinatari" a cui è applicabile il token.
expiration timestamp Ora di scadenza del token.

La proprietà name identifica l'entità a cui deve essere associato il token. Nel bus di servizio corrisponde al percorso della coda o dell'argomento/sottoscrizione. La proprietà type identifica il tipo di token:

Tipo di token Descrizione del token Tipo di corpo Note
jwt Token JSON Web (JWT) Valore AMQP (stringa)
servicebus.windows.net:sastoken Token SAS del bus di servizio Valore AMQP (stringa) -

I token conferiscono diritti. Il bus di servizio riconosce tre diritti fondamentali: "Send" per consentire l'invio, "Listen" per consentire la ricezione e "Manage" per consentire la modifica delle entità. I token di firma di accesso condiviso del bus di servizio fanno riferimento a regole configurate nello spazio dei nomi o nell'entità e queste regole sono configurate con diritti. Se si firma il token con la chiave associata a quella regola, il token esprimerà i rispettivi diritti. Il token associato a un'entità che usa put-token consente al client connesso di interagire con l'entità in base ai diritti del token. Un collegamento in cui il client ha il ruolo sender richiede il diritto "Send", mentre per il ruolo receiver richiede il diritto "Listen".

Il messaggio di risposta ha i valori application-properties seguenti:

Chiave Facoltativo Tipo di valore Contenuti del valore
status-code No int Codice di risposta HTTP [RFC2616].
status-description stringa Descrizione dello stato.

Il client può chiamare put-token ripetutamente e per qualsiasi entità nell'infrastruttura di messaggistica. I token hanno come ambito il client corrente e sono ancorati alla connessione corrente, quindi il server elimina eventuali token conservati al termine della connessione.

L'implementazione corrente bus di servizio consente solo CBS insieme al metodo SASL "ANONYMOUS". Una connessione SSL/TLS deve essere sempre presente prima dell'handshake SASL.

Il meccanismo ANONYMOUS deve quindi essere supportato dal client AMQP 1.0 scelto. Per accesso anonimo si intende che l'handshake di connessione iniziale, inclusa la creazione della sessione iniziale, viene eseguito senza che il bus di servizio sappia chi sta creando la connessione.

Dopo aver stabilito la connessione e la sessione, le uniche operazioni consentite sono l'associazione dei collegamenti al nodo $cbs e l'invio della richiesta put-token. Un token valido deve essere impostato correttamente usando una richiesta put-token per un nodo di entità entro 20 secondi dall'avvio della connessione. In caso contrario, la connessione viene interrotta unilateralmente dal bus di servizio.

Il client è successivamente responsabile della verifica della scadenza del token. Alla scadenza di un token il bus di servizio elimina immediatamente tutti i collegamenti alla rispettiva entità nella connessione. Per evitare questo problema, il client può sostituire il token per il nodo con un nuovo token in qualsiasi momento tramite il nodo di gestione virtuale $cbs con lo stesso gesto put-token e senza ostacolare il traffico di payload tra i diversi collegamenti.

Funzionalità di invio tramite

Invio tramite/Trasferisci mittente è una funzionalità che consente ai bus di servizio di inoltrare un determinato messaggio all'entità di destinazione tramite un'altra entità. Questa funzione viene usata principalmente per eseguire operazioni tra le entità in una singola transazione.

Con questa funzionalità, si crea un mittente e si stabilisce il collegamento a via-entity. Durante il tentativo di stabilire il collegamento, vengono trasmesse informazioni aggiuntive per stabilire la destinazione reale dei messaggi/trasferimenti a questo collegamento. Dopo che il collegamento è stato eseguito correttamente, tutti i messaggi inviati su questo collegamento vengono inoltrati automaticamente all'entità di destinazione mediante tramite entità.

Nota: l'autenticazione server deve essere eseguita sia per Entità tramite e Entità di destinazione prima di stabilire il collegamento.

Client Orientamento Bus di servizio
attach(<br/>name={link name},<br/>role=sender,<br/>source={client link ID},<br/>target=**{via-entity}**,<br/>**properties=map [(<br/>com.microsoft:transfer-destination-address=<br/>{destination-entity} )]** ) ------>
<------ attach(<br/>name={link name},<br/>role=receiver,<br/>source={client link ID},<br/>target={via-entity},<br/>properties=map [(<br/>com.microsoft:transfer-destination-address=<br/>{destination-entity} )] )

Passaggi successivi

Per altre informazioni su AMQP, vedere bus di servizio panoramica di AMQP.