Considerazioni sulla codifica dei messaggi
Molte applicazioni cloud usano messaggi asincroni per scambiare informazioni tra i componenti del sistema. Un aspetto importante della messaggistica è il formato usato per codificare i dati del payload. Dopo aver scelto una tecnologia di messaggistica, il passaggio successivo consiste nel definire la modalità di codifica dei messaggi. Sono disponibili molte opzioni, ma la scelta giusta dipende dal caso d'uso.
Questo articolo descrive alcune considerazioni.
Esigenze di scambio di messaggi
Uno scambio di messaggi tra un produttore e un consumatore richiede:
- Forma o struttura che definisce il payload del messaggio.
- Formato di codifica per rappresentare il payload.
- Librerie di serializzazione per leggere e scrivere il payload codificato.
Il produttore del messaggio definisce la forma del messaggio in base alla logica aziendale e alle informazioni che desidera inviare ai consumatori. Per strutturare la forma, dividere le informazioni in soggetti discreti o correlati (campi). Decidere le caratteristiche dei valori per tali campi. Si consideri: qual è il tipo di dati più efficiente? Il payload avrà sempre determinati campi? Il payload avrà un singolo record o un set ripetuto di valori?
Scegliere quindi un formato di codifica in base alle esigenze. Alcuni fattori includono la possibilità di creare dati altamente strutturati, se necessario, tempo impiegato per codificare e trasferire il messaggio e la possibilità di analizzare il payload. A seconda del formato di codifica, scegliere una libreria di serializzazione ben supportata.
Un consumer del messaggio deve essere a conoscenza di tali decisioni in modo che sappia come leggere i messaggi in arrivo.
Per trasferire i messaggi, il produttore serializza il messaggio in un formato codificato. Al termine della ricezione, il consumatore deserializza il payload per utilizzare i dati. In questo modo entrambe le entità condividono il modello e, purché la forma non cambi, la messaggistica continua senza problemi. Quando il contratto cambia, il formato di codifica deve essere in grado di gestire la modifica senza interrompere il cliente.
Alcuni formati di codifica, ad esempio JSON, sono autodescrittura, ovvero possono essere analizzati senza fare riferimento a uno schema. Tuttavia, tali formati tendono a produrre messaggi più grandi. Con altri formati, i dati potrebbero non essere analizzati con facilità, ma i messaggi sono compattati. Questo articolo evidenzia alcuni fattori che consentono di scegliere un formato.
Considerazioni sul formato di codifica
Il formato di codifica definisce il modo in cui un set di dati strutturati viene rappresentato come byte. Il tipo di messaggio può influenzare la scelta del formato. I messaggi correlati alle transazioni aziendali conterranno probabilmente dati altamente strutturati. È anche possibile recuperarlo in un secondo momento a scopo di controllo. Per un flusso di eventi, è possibile leggere una sequenza di record il più rapidamente possibile e archiviarla per l'analisi statistica.
Ecco alcuni punti da considerare quando si sceglie un formato di codifica.
Leggibilità umana
La codifica dei messaggi può essere suddivisa in modo ampio in formati binari e basati su testo.
Con la codifica basata su testo, il payload del messaggio è in testo normale e pertanto può essere controllato da una persona senza usare librerie di codice. I formati leggibili sono adatti per i dati di archiviazione. Inoltre, poiché un essere umano può leggere il payload, i formati basati su testo sono più facili da usare per il debug e per registrare nei log per la risoluzione degli errori.
Lo svantaggio è che il carico utile tende a essere più grande. Un formato comune basato su testo è JSON.
Crittografia
Se nei messaggi sono presenti dati sensibili, valutare se tali messaggi devono essere crittografati interamente come descritto nella guida sulla crittografia dei dati a riposo di Azure Service Bus. In alternativa, se è necessario crittografare solo determinati campi e si preferisce ridurre i costi del cloud, è consigliabile usare una libreria come NServiceBus.
Dimensioni della codifica
Le dimensioni dei messaggi influiscono sulle prestazioni di I/O di rete attraverso la rete. I formati binari sono più compatti rispetto ai formati basati su testo. I formati binari richiedono librerie di serializzazione/deserializzazione. Il payload non può essere letto a meno che non sia decodificato.
Usa un formato binario se vuoi ridurre l'impronta sulla rete e trasferire i messaggi più velocemente. Questa categoria di formato è consigliata negli scenari in cui l'archiviazione o la larghezza di banda di rete è un problema. Le opzioni per i formati binari includono Apache Avro, Google Protocol Buffers (protobuf), MessagePack e Concise Binary Object Representation (CBOR). I vantaggi e gli svantaggi di questi formati sono descritti nella Sezione .
Lo svantaggio è che il payload non è leggibile per l'uomo. La maggior parte dei formati binari usa sistemi complessi che possono essere costosi da gestire. Inoltre, sono necessarie librerie specializzate per decodificare, che potrebbero non essere supportate se si desidera recuperare i dati di archiviazione.
Informazioni sul payload
Il payload di un messaggio arriva come una sequenza di byte. Per analizzare questa sequenza, il consumer deve avere accesso ai metadati che descrivono i campi dati nel payload. Esistono due approcci principali per l'archiviazione e la distribuzione dei metadati:
metadati taggati. In alcune codifiche, in particolare JSON, i campi vengono contrassegnati con il tipo di dati e l'identificatore, all'interno del corpo del messaggio. Questi formati sono auto-descrittivi perché possono essere analizzati in un dizionario di valori senza fare riferimento a uno schema. Un modo per consentire al consumer di comprendere i campi consiste nell'eseguire query sui valori previsti. Ad esempio, il producer invia un payload in JSON. Il consumatore analizza il JSON in un dizionario e verifica l'esistenza dei campi per comprendere il payload. Un altro modo consiste nell'applicare un modello di dati condiviso dal produttore. Ad esempio, se si usa un linguaggio tipizzato in modo statico, molte librerie di serializzazione JSON possono analizzare una stringa JSON in una classe tipizzata.
schema. Uno schema definisce formalmente la struttura e i campi dati di un messaggio. In questo modello, il producer e il consumer hanno un contratto tramite uno schema ben definito. Lo schema può definire i tipi di dati, i campi obbligatori/facoltativi, le informazioni sulla versione e la struttura del payload. Il produttore invia il payload in base allo schema dello scrittore. Il consumatore riceve il payload applicando uno schema di lettura. Il messaggio viene serializzato/deserializzato usando le librerie specifiche della codifica. Esistono due modi per distribuire gli schemi:
Archivia lo schema come preambolo o intestazione nel messaggio, ma tienilo separato dal payload.
Archiviare lo schema esternamente.
Alcuni formati di codifica definiscono lo schema e usano strumenti che generano classi dallo schema. Il produttore e il consumatore usano queste classi e librerie per serializzare e deserializzare il payload. Le librerie forniscono anche controlli di compatibilità tra lo schema writer e il lettore. Sia protobuf che Apache Avro seguono questo approccio. La differenza principale è che protobuf ha una definizione di schema indipendente dal linguaggio, ma Avro usa JSON compatto. Un'altra differenza consiste nel modo in cui entrambi i formati forniscono controlli di compatibilità tra gli schemi reader e writer.
Un altro modo per archiviare lo schema esternamente in un registro schemi. Il messaggio contiene un riferimento allo schema e al payload. Il produttore invia l'identificatore dello schema nel messaggio e il consumatore recupera lo schema specificando tale identificatore da un archivio esterno. Entrambe le parti usano una libreria specifica per il formato per leggere e scrivere messaggi. Oltre all'archiviazione dello schema, un Registro di sistema può fornire controlli di compatibilità per assicurarsi che il contratto tra il producer e il consumer non venga interrotto man mano che lo schema si evolve.
Prima di scegliere un approccio, decidere cosa è più importante: la dimensione dei dati di trasferimento o la possibilità di analizzare i dati archiviati in un secondo momento.
L'archiviazione dello schema insieme al payload produce una dimensione di codifica maggiore ed è preferibile per i messaggi intermittenti. Scegliere questo approccio se il trasferimento di blocchi di byte più piccoli è fondamentale o si prevede una sequenza di record. Il costo della gestione di un archivio schemi esterno può essere elevato.
Tuttavia, se la decodifica su richiesta del payload è più importante delle dimensioni, incluso lo schema con il payload o l'approccio ai metadati contrassegnati garantisce la decodifica successiva. Potrebbe esserci un aumento significativo delle dimensioni dei messaggi e potrebbe influire sul costo dell'archiviazione.
Controllo delle versioni dello schema
Man mano che cambiano i requisiti aziendali, la forma dovrebbe cambiare e lo schema si evolverà. Il controllo delle versioni consente al producer di indicare gli aggiornamenti dello schema che potrebbero includere nuove funzionalità. Esistono due aspetti del controllo delle versioni:
Il consumer deve essere a conoscenza delle modifiche.
Un modo è consentire al consumer di controllare tutti i campi per determinare se lo schema è stato modificato. Un altro modo consiste nel far sì che il produttore pubblichi un numero di versione dello schema con il messaggio. Quando lo schema si evolve, il produttore aumenta la versione.
Le modifiche non devono influire o interrompere la logica aziendale dei consumatori.
Si supponga che un campo venga aggiunto a uno schema esistente. Se gli utenti che utilizzano la nuova versione ricevono un payload conforme alla versione precedente, potrebbero verificarsi problemi nella logica se non riescono a ignorare l'assenza del nuovo campo. Considerando il caso inverso, si supponga che un campo venga rimosso nel nuovo schema. I consumer che usano lo schema precedente potrebbero non essere in grado di leggere i dati.
I formati di codifica, ad esempio Avro, offrono la possibilità di definire i valori predefiniti. Nell'esempio precedente, se il campo viene aggiunto con un valore predefinito, il campo mancante verrà popolato con il valore predefinito. Altri formati, ad esempio protobuf, offrono funzionalità simili tramite campi obbligatori e facoltativi.
Struttura del payload
Considerare la modalità di disposizione dei dati nel payload. Si tratta di una sequenza di record o di un singolo payload discreto? La struttura del payload può essere suddivisa in uno di questi modelli:
Matrice/dizionario/valore: definisce le voci che contengono valori in una o matrici multidimensionali. Le voci hanno coppie chiave-valore univoche. Può essere esteso per rappresentare le strutture complesse. Alcuni esempi includono, JSON, Apache Avro e MessagePack.
Questo layout è adatto se i messaggi sono codificati singolarmente con schemi diversi. Se si dispone di più record, il payload può diventare eccessivamente ridondante causando un ingrossamento del payload.
Dati tabulari: le informazioni sono suddivise in righe e colonne. Ogni colonna indica un campo o l'oggetto delle informazioni e ogni riga contiene valori per tali campi. Questo layout è efficiente per un set ripetuto di informazioni, ad esempio i dati delle serie temporali.
CSV è uno dei formati più semplici basati su testo. Presenta i dati come sequenza di record con un'intestazione comune. Per la codifica binaria, Apache Avro ha un preambolo simile a un'intestazione CSV, ma genera dimensioni di codifica compatta.
Supporto della libreria
Prendere in considerazione l'uso di formati noti piuttosto che di un modello proprietario.
I formati noti sono supportati tramite librerie largamente supportate dalla comunità. Con formati specializzati, sono necessarie librerie specifiche. La logica di business potrebbe dover aggirare alcune delle scelte di progettazione dell'API fornite dalle librerie.
Per il formato basato su schema, scegliere una libreria di codifica che effettua verifiche di compatibilità tra lo schema lettore e scrittore. Alcune librerie di codifica, come Apache Avro, prevedono che il consumatore specifichi sia lo schema dello scrittore che quello del lettore prima di deserializzare il messaggio. Questo controllo garantisce che il consumatore sia a conoscenza delle versioni dello schema.
Interoperabilità
La scelta dei formati può dipendere dal carico di lavoro o dall'ecosistema tecnologico specifico.
Per esempio:
Analisi di flusso di Azure offre il supporto nativo per JSON, CSV e Avro. Quando si usa Analisi di flusso, è opportuno scegliere uno di questi formati, se possibile. In caso contrario, è possibile fornire un deserializzatore personalizzato , ma questo aggiunge una certa complessità alla soluzione.
JSON è un formato di interscambio standard per le API REST HTTP. Se l'applicazione riceve payload JSON dai client e le inserisce in una coda di messaggi per l'elaborazione asincrona, potrebbe essere opportuno usare JSON per la messaggistica, anziché ricodicerla in un formato diverso.
Questi sono solo due esempi di considerazioni sull'interoperabilità. In generale, i formati standardizzati saranno più interoperabili rispetto ai formati personalizzati. Nelle opzioni basate su testo, JSON è uno dei più interoperabili.
Scelte per i formati di codifica
Ecco alcuni formati di codifica più diffusi. Tenere presenti le considerazioni prima di scegliere un formato.
JSON
JSON è uno standard aperto (IETF RFC8259). Si tratta di un formato basato su testo che segue il modello matrice/dizionario/valore.
JSON può essere usato per l'assegnazione di tag ai metadati ed è possibile analizzare il payload senza uno schema. JSON supporta l'opzione per specificare campi facoltativi, che semplificano la compatibilità con le versioni precedenti e successive.
Il vantaggio più grande è che è disponibile ovunque. È più interoperabile e il formato di codifica predefinito per molti servizi di messaggistica.
Essendo un formato basato su testo, non è efficiente in rete e non è una scelta ideale nei casi in cui lo spazio di archiviazione è un problema. Se si restituiscono elementi memorizzati nella cache direttamente a un client tramite HTTP, l'archiviazione di JSON potrebbe ridurre il costo della deserializzazione da un altro formato e quindi la serializzazione in JSON.
Usare JSON per i messaggi a record singolo o per una sequenza di messaggi in cui ogni messaggio ha uno schema diverso. Evitare di usare JSON per una sequenza di record, ad esempio per i dati delle serie temporali.
Esistono altre varianti di JSON, ad esempio JSON binario (BSON), che è una codifica binaria allineata per l'uso con MongoDB.
Valori Comma-Separated (CSV)
CSV è un formato tabulare basato su testo. L'intestazione della tabella indica i campi. È una scelta preferita in cui il messaggio contiene un set di record.
Lo svantaggio è la mancanza di standardizzazione. Esistono molti modi per esprimere separatori, intestazioni e campi vuoti.
Protocol Buffers (protobuf)
Protocol Buffers (o protobuf) è un formato di serializzazione che utilizza file di definizione fortemente tipizzati per definire gli schemi di coppie chiave/valore. Questi file di definizione vengono quindi compilati in classi specifiche del linguaggio usate per serializzare e deserializzare i messaggi.
Il messaggio contiene un payload binario compresso di piccole dimensioni, che consente un trasferimento più rapido. Lo svantaggio è che il payload non è leggibile da un essere umano. Inoltre, poiché lo schema è esterno, non è consigliabile per i casi in cui è necessario recuperare i dati archiviati.
Apache Avro
Apache Avro è un formato di serializzazione binaria che usa un file di definizione simile a protobuf, ma non esiste un passaggio di compilazione. I dati serializzati invece includono sempre un preambolo dello schema.
Il preambolo può contenere l'intestazione o un identificatore dello schema. A causa delle dimensioni di codifica inferiori, Avro è consigliato per lo streaming dei dati. Inoltre, poiché ha un'intestazione che si applica a un set di record, è una scelta ottimale per i dati tabulari.
MessagePack
MessagePack è un formato di serializzazione binaria progettato per essere compatto per la trasmissione in rete. Non sono presenti schemi di messaggio o controllo del tipo di messaggio. Questo formato non è consigliato per l'archiviazione bulk.
CBOR
CBOR (Concise Binary Object Representation) (Specification) è un formato binario che offre dimensioni di codifica ridotte. Il vantaggio di CBOR rispetto a MessagePack è che è conforme a IETF in RFC7049.