Considerações sobre codificação de mensagens
Muitos aplicativos de nuvem usam mensagens assíncronas para trocar informações entre componentes do sistema. Um aspecto importante do sistema de mensagens é o formato usado para codificar os dados de conteúdo. Depois que você escolher uma tecnologia de sistema de mensagens, a próxima etapa será definir como as mensagens serão codificadas. Há muitas opções disponíveis, mas a escolha certa depende do seu caso de uso.
Este artigo descreve algumas dessas considerações.
Necessidades de troca de mensagens
Uma troca de mensagens entre um produtor e um consumidor precisa do seguinte:
- Uma forma ou uma estrutura que define o conteúdo da mensagem.
- Um formato de codificação para representar o conteúdo.
- Bibliotecas de serialização para ler e gravar o conteúdo codificado.
O produtor da mensagem define a forma da mensagem com base na lógica de negócios e nas informações que deseja enviar aos consumidores. Para estruturar a forma, divida as informações em assuntos discretos ou relacionados (campos). Decida as características dos valores para esses campos. Considere: qual é o tipo de dados mais eficiente? O conteúdo sempre terá determinados campos? O conteúdo terá um só registro ou um conjunto repetido de valores?
Em seguida, escolha um formato de codificação dependendo da sua necessidade. Alguns fatores incluem a capacidade de criar dados altamente estruturados, se necessário, o tempo usado para codificar e transferir a mensagem e a capacidade de analisar o conteúdo. Dependendo do formato de codificação, escolha uma biblioteca de serialização com bom suporte.
Um consumidor da mensagem precisa estar ciente dessas decisões de modo que saiba como ler as mensagens de entrada.
Para transferir mensagens, o produtor serializa a mensagem em um formato de codificação. No final do recebimento, o consumidor desserializa o conteúdo para usar os dados. Dessa forma, as duas entidades compartilham o modelo e, desde que a forma não mude, o sistema de mensagens continuará não apresentando problemas. Quando o contrato é alterado, o formato de codificação deve ter a capacidade de lidar com a alteração sem interromper o consumidor.
Alguns formatos de codificação, como o JSON, são autoexplicativos, o que significa que eles podem ser analisados sem referenciar um esquema. No entanto, esses formatos tendem a gerar mensagens maiores. Com outros formatos, os dados podem não ser analisados com tanta facilidade, mas as mensagens são compactas. Este artigo destaca alguns fatores que podem ajudar você a escolher um formato.
Considerações sobre o formato de codificação
O formato de codificação define como um conjunto de dados estruturados é representado como bytes. O tipo de mensagem pode influenciar a escolha de formato. As mensagens relacionadas a transações de negócios provavelmente conterão dados altamente estruturados. Além disso, o ideal é recuperá-los posteriormente para fins de auditoria. Para um fluxo de eventos, recomendamos ler uma sequência de registros o mais rápido possível e armazená-la para análise estatística.
Estes são alguns pontos a serem considerados na escolha de um formato de codificação.
Capacidade de leitura humana
A codificação de mensagens pode ser amplamente dividida em formatos binários e baseados em texto.
Com a codificação baseada em texto, o conteúdo da mensagem está em um texto sem formatação e, portanto, pode ser inspecionado por uma pessoa sem o uso de nenhuma biblioteca de código. Formatos legíveis por humanos são adequados para dados de arquivamento. Além disso, como uma pessoa pode ler o conteúdo, os formatos baseados em texto são mais fáceis de serem depurados e enviados para os logs a fim de solucionar erros.
A desvantagem é que o conteúdo tende a ser maior. Um formato comum baseado em texto é o JSON.
Criptografia
Se houver dados confidenciais nas mensagens, considere se essas mensagens devem ser totalmente criptografadas, conforme descrito nesta orientação sobre como criptografar dados do Barramento de Serviço do Azure em repouso. Como alternativa, se apenas determinados campos precisarem ser criptografados e você preferir reduzir os custos de nuvem, considere usar uma biblioteca como NServiceBus para isso.
Tamanho da codificação
O tamanho da mensagem afeta o desempenho de E/S da rede em toda a transmissão. Os formatos binários são mais compactos do que os formatos baseados em texto. Os formatos binários exigem bibliotecas de serialização/desserialização. O conteúdo não pode ser lido, a menos que seja decodificado.
Use um formato binário se quiser reduzir o volume de transmissão e transferir mensagens mais rapidamente. Essa categoria de formato é recomendada em cenários em que o armazenamento ou a largura de banda de rede é uma preocupação. As opções de formatos binários incluem o Apache Avro, os Buffers de Protocolo do Google (protobuf), o MessagePack e o CBOR (Representação Concisa de Objetos Binários). Os prós e os contras desses formatos são descritos nesta seção.
A desvantagem é que o conteúdo não é legível por humanos. A maioria dos formatos binários usa sistemas complexos que podem ter uma manutenção cara. Além disso, eles precisam de bibliotecas especializadas para decodificação, que podem não ter suporte se você quer recuperar os dados de arquivamento.
Noções básicas sobre o conteúdo
Um conteúdo de mensagem chega como uma sequência de bytes. Para analisar essa sequência, o consumidor precisa ter acesso aos metadados que descrevem os campos de dados no conteúdo. Há duas abordagens principais para armazenar e distribuir metadados:
Metadados marcados. Em algumas codificações, principalmente JSON, os campos são marcados com o tipo de dados e o identificador, no corpo da mensagem. Esses formatos são autoexplicativos, porque podem ser analisados em um dicionário de valores sem se referirem a um esquema. Uma forma para o consumidor entender os campos é consultar os valores esperados. Por exemplo, o produtor envia um conteúdo em JSON. O consumidor analisará o JSON em um dicionário e verificará a existência de campos para entender o conteúdo. Outra maneira é que o consumidor aplique um modelo de dados compartilhado pelo produtor. Por exemplo, se você estiver usando uma linguagem tipada estaticamente, muitas bibliotecas de serialização JSON poderão analisar uma cadeia de caracteres JSON em uma classe tipada.
Esquema. Um esquema define formalmente a estrutura e os campos de dados de uma mensagem. Nesse modelo, o produtor e o consumidor têm um contrato por meio de um esquema bem definido. O esquema pode definir os tipos de dados, os campos obrigatórios/opcionais, as informações de versão e a estrutura do conteúdo. O produtor envia o conteúdo de acordo com o esquema do autor. O consumidor recebe o conteúdo aplicando um esquema de leitor. A mensagem é serializada/desserializada por meio das bibliotecas específicas de codificação. Há duas maneiras de distribuir os esquemas:
Armazenar o esquema como um preâmbulo ou um cabeçalho na mensagem, mas separado do conteúdo.
Armazenar o esquema externamente.
Alguns formatos de codificação definem o esquema e usam ferramentas que geram classes com base no esquema. O produtor e o consumidor usam essas classes e bibliotecas para serializar e desserializar o conteúdo. As bibliotecas também fornecem verificações de compatibilidade entre o esquema do autor e do leitor. O protobuf e o Apache Avro seguem essa abordagem. A principal diferença é que o protobuf tem uma definição de esquema independente de linguagem, mas o Avro usa um JSON compacto. Outra diferença está na maneira como os dois formatos fornecem verificações de compatibilidade entre os esquemas de leitor e de autor.
Outra maneira de armazenar o esquema externamente é em um registro de esquema. A mensagem contém uma referência ao esquema e ao conteúdo. O produtor envia o identificador de esquema na mensagem e o consumidor recupera o esquema especificando esse identificador de um armazenamento externo. As duas partes usam a biblioteca específica do formato para ler e gravar mensagens. Além de armazenar o esquema, um registro pode fornecer verificações de compatibilidade para garantir que o contrato entre o produtor e o consumidor não seja interrompido à medida que o esquema evolui.
Antes de escolher uma abordagem, decida o que é mais importante: o tamanho dos dados de transferência ou a capacidade de analisar os dados arquivados posteriormente.
Armazenar o esquema junto com o conteúdo gera um tamanho maior de codificação e é preferencial para mensagens intermitentes. Escolha essa abordagem se a transferência de partes menores de bytes for crucial ou se você esperar uma sequência de registros. O custo de manter um armazenamento de esquema externo pode ser alto.
No entanto, se a decodificação sob demanda do conteúdo for mais importante do que o tamanho, incluir o esquema com o conteúdo ou a abordagem de metadados marcados garantirá a decodificação posteriormente. Pode haver um aumento significativo no tamanho da mensagem e isso pode afetar o custo do armazenamento.
Controle de versão do esquema
À medida que os requisitos de negócios mudam, é esperado que a forma mude e o esquema evolua. O controle de versão permite que o produtor indique as atualizações de esquema que possam incluir novos recursos. Há dois aspectos do controle de versão:
O consumidor deve estar ciente das alterações.
Uma forma é que o consumidor verifique todos os campos para determinar se o esquema foi alterado. Outra maneira é que o produtor publique um número de versão de esquema com a mensagem. Quando o esquema evolui, o produtor incrementa a versão.
As alterações não devem afetar nem interromper a lógica de negócios dos consumidores.
Suponha que um campo seja adicionado a um esquema existente. Se os consumidores que usam a nova versão receberem um conteúdo de acordo com a versão antiga, a lógica deles poderá ser interrompida se eles não conseguirem ignorar a falta do novo campo. Considerando o caso inverso, suponha que um campo seja removido do novo esquema. Os consumidores que usam o esquema antigo podem não conseguir ler os dados.
Formatos de codificação, como o Avro, oferecem a capacidade de definir valores padrão. No exemplo anterior, se o campo for adicionado com um valor padrão, o campo ausente será preenchido com o valor padrão. Outros formatos, como o protobuf, fornecem uma funcionalidade semelhante por meio de campos obrigatórios e opcionais.
Estrutura do conteúdo
Considere a maneira como os dados são organizados no conteúdo. É uma sequência de registros ou um conteúdo único discreto? A estrutura do conteúdo pode ser categorizada em um destes modelos:
Matriz/dicionário/valor: define entradas que contém valores em matrizes de uma dimensão ou multidimensionais. As entradas têm pares chave-valor exclusivos. Isso pode ser estendido para representar as estruturas complexas. Alguns exemplos incluem JSON, Apache Avro e MessagePack.
Esse layout é adequado se as mensagens são codificadas individualmente com esquemas diferentes. Se você tiver vários registros, o conteúdo poderá ficar muito redundante, fazendo com que o conteúdo fique sobrecarregado.
Dados de tabela: as informações são divididas em linhas e colunas. Cada coluna indica um campo ou o assunto das informações, e cada linha contém valores para esses campos. Esse layout é eficiente para um conjunto repetido de informações, como dados de série temporal.
O CSV é um dos formatos mais simples baseados em texto. Ele apresenta os dados como uma sequência de registros com um cabeçalho comum. Para a codificação binária, o Apache Avro tem um preâmbulo semelhante a um cabeçalho CSV, mas gera um tamanho de codificação compacto.
Suporte à biblioteca
Considere o uso de formatos conhecidos em vez de um modelo proprietário.
Os formatos conhecidos são compatíveis por meio de bibliotecas que têm o suporte universal da comunidade. Com formatos especializados, você precisa ter bibliotecas específicas. Sua lógica de negócios pode precisar resolver algumas das escolhas de design de API fornecidas pelas bibliotecas.
Para o formato baseado em esquema, escolha uma biblioteca de codificação que faça verificações de compatibilidade entre o esquema de leitor e de autor. Algumas bibliotecas de codificação, como o Apache Avro, esperam que o consumidor especifique o esquema de autor e de leitor antes de desserializar a mensagem. Essa verificação garante que o consumidor esteja ciente das versões de esquema.
Interoperabilidade
Sua escolha de formatos pode depender da carga de trabalho específica ou do ecossistema de tecnologia.
Por exemplo:
O Azure Stream Analytics tem suporte nativo para JSON, CSV e Avro. Durante o uso do Stream Analytics, faz sentido escolher um desses formatos, se possível. Caso contrário, você pode fornecer um desserializador personalizado, mas isso adiciona alguma complexidade adicional à sua solução.
O JSON é um formato de troca padrão para APIs REST HTTP. Se o seu aplicativo receber conteúdo JSON de clientes e colocá-lo em uma fila de mensagens para processamento assíncrono, poderá fazer sentido usar o JSON para o sistema de mensagens, em vez de fazer a codificação em um formato diferente.
Esses são apenas dois exemplos de considerações sobre interoperabilidade. Em geral, os formatos padronizados serão mais interoperáveis do que os formatos personalizados. Em opções baseadas em texto, o JSON é um dos mais interoperáveis.
Opções de formatos de codificação
Estes são alguns formatos de codificação populares. Leve em conta as considerações antes de escolher um formato.
JSON
O JSON é um padrão aberto (IETF RFC8259). É um formato baseado em texto que segue o modelo de matriz/dicionário/valor.
O JSON pode ser usado para marcar metadados, e você pode analisar o conteúdo sem um esquema. Ele dá suporte à opção de especificar campos opcionais, o que ajuda na compatibilidade com versões anteriores e posteriores.
A maior vantagem é que ele está disponível universalmente. Ele é mais interoperável e o formato de codificação padrão para muitos serviços de mensagens.
Sendo um formato baseado em texto, ele não é eficiente durante a transmissão e não é uma opção ideal nos casos em que o armazenamento é uma preocupação. Se você estiver retornando itens armazenados em cache diretamente para um cliente por meio de HTTP, o armazenamento de JSON poderá reduzir os custos da desserialização de outro formato e serialização em JSON.
Use o JSON para mensagens de registro único ou para uma sequência de mensagens em que cada mensagem tem um esquema diferente. Evite usar o JSON para uma sequência de registros, como para dados de série temporal.
Há outras variações do JSON, como o BSON (JSON binário), que é uma codificação binária alinhada para trabalhar com o MongoDB.
CSV (valores separados por vírgula)
O CSV é um formato de tabela baseado em texto. O cabeçalho da tabela indica os campos. É uma opção preferencial para os casos em que a mensagem contém um conjunto de registros.
A desvantagem é a falta de padronização. Há muitas maneiras de expressar separadores, cabeçalhos e campos vazios.
Buffers de protocolo (protobuf)
Os Buffers de Protocolo (ou protobuf) são um formato de serialização que usa arquivos de definição fortemente tipados para definir esquemas em pares chave/valor. Em seguida, esses arquivos de definição são compilados em classes específicas de linguagem que são usadas para serializar e desserializar as mensagens.
A mensagem contém um conteúdo binário pequeno compactado, que resulta em uma transferência mais rápida. A desvantagem é que o conteúdo não é legível por humanos. Além disso, como o esquema é externo, ele não é recomendado para os casos em que você precisa recuperar os dados arquivados.
Apache Avro
O Apache Avro é um formato de serialização binária que usa um arquivo de definição semelhante ao protobuf, mas não há uma etapa de compilação. Em vez disso, os dados serializados sempre incluem um preâmbulo de esquema.
O preâmbulo pode conter o cabeçalho ou um identificador de esquema. Devido ao tamanho menor da codificação, o Avro é recomendado para transmitir dados. Além disso, como ele tem um cabeçalho que se aplica a um conjunto de registros, é uma boa opção para dados de tabela.
MessagePack
MessagePack é um formato de serialização binária projetado para ser compacto para transmissão eletrônica. Não existem esquemas de mensagens ou verificação de tipo de mensagem. Esse formato não é recomendado para o armazenamento em massa.
CBOR
O CBOR (Representação Concisa de Objetos Binários) (especificação) é um formato binário que oferece um tamanho de codificação pequeno. A vantagem do CBOR em relação ao MessagePack é que ele está em conformidade com o IETF na RFC7049.
Próximas etapas
- Noções básicas sobre os padrões de design de mensagens para aplicativos de nuvem.