Compartir a través de


Consideraciones acerca de la codificación de mensajes

Muchas aplicaciones en la nube usan mensajes asincrónicos para intercambiar información entre los componentes del sistema. Un aspecto importante de la mensajería es el formato usado para codificar los datos de carga. Después de elegir una tecnología de mensajería, el siguiente paso es definir cómo se codificarán los mensajes. Hay muchas opciones disponibles, pero la opción adecuada depende de su caso de uso.

En este artículo se describen algunas de las consideraciones.

Necesidades de intercambio de mensajes

Un intercambio de mensajes entre un productor y un consumidor necesita:

  • Forma o estructura que define la carga del mensaje.
  • Formato de codificación para representar la carga útil.
  • Bibliotecas de serialización para leer y escribir la carga codificada.

El productor del mensaje define la forma del mensaje en función de la lógica de negocios y la información que desea enviar a los consumidores. Para estructurar la forma, divida la información en temas discretos o relacionados (campos). Decida las características de los valores de esos campos. Debe tener en cuenta las siguientes preguntas.

  • ¿Cuál es el tipo de datos más eficaz?
  • ¿La carga siempre tiene determinados campos?
  • ¿La carga tiene un único registro o un conjunto repetido de valores?

A continuación, elija un formato de codificación en función de sus necesidades. Algunos factores incluyen la capacidad de crear datos altamente estructurados si lo necesita, el tiempo necesario para codificar y transferir el mensaje y la capacidad de analizar la carga. En función del formato de codificación, elija una biblioteca de serialización compatible.

Un consumidor del mensaje debe tener en cuenta esas decisiones para que sepa cómo leer los mensajes entrantes.

Para transferir mensajes, el productor serializa el mensaje a un formato de codificación. En el extremo receptor, el consumidor deserializa la carga útil para utilizar los datos. De este modo, ambas entidades comparten el modelo y, siempre y cuando la forma no cambie, la mensajería continúa sin problemas. Cuando cambia el contrato, el formato de codificación debe ser capaz de controlar el cambio sin interrumpir el consumidor.

Algunos formatos de codificación, como JSON, se describen automáticamente, lo que significa que se pueden analizar sin hacer referencia a un esquema. Sin embargo, estos formatos tienden a producir mensajes más grandes. Con otros formatos, es posible que los datos no se analicen tan fácilmente, pero los mensajes son compactos. En este artículo se resaltan algunos factores que pueden ayudarle a elegir un formato.

Consideraciones sobre el formato de codificación

El formato de codificación define cómo se representa un conjunto de datos estructurados como bytes. El tipo de mensaje puede influir en la opción de formato. Los mensajes relacionados con las transacciones empresariales probablemente contienen datos altamente estructurados. Además, puede que quiera recuperarlo más adelante con fines de auditoría. En el caso de un flujo de eventos, es posible que desee leer una secuencia de registros lo más rápido posible y almacenarlo para el análisis estadístico.

Estos son algunos puntos que se deben tener en cuenta al elegir un formato de codificación.

Legibilidad para los usuarios

La codificación de mensajes se puede dividir ampliamente en formatos binarios y basados en texto.

Con la codificación basada en texto, la carga del mensaje está en texto sin formato y, por tanto, una persona puede inspeccionarla sin usar ninguna biblioteca de código. Los formatos legibles son adecuados para los datos de archivo. Además, dado que un usuario puede leer la carga, los formatos basados en texto son más fáciles de depurar y enviar a los registros para solucionar errores.

La desventaja es que la carga tiende a ser mayor. El tamaño de la carga a menudo se puede reducir a través de un proceso de minificación, siempre que se pueda invertir para la legibilidad humana cuando sea necesario. Los formatos comunes basados en texto son JSON y YAML.

Cifrado

Si hay datos confidenciales en los mensajes, considere si esos mensajes se deben cifrar en su totalidad, tal como se describe en esta guía sobre cifrar los datos de Azure Service Bus en reposo. Como alternativa, si solo es necesario cifrar determinados campos y prefiere reducir los costos en la nube, considere la posibilidad de usar una biblioteca como NServiceBus para ello.

Tamaño de codificación

El tamaño del mensaje afecta al rendimiento de E/S de red a través de la conexión. Los formatos binarios son más compactos que los formatos basados en texto. Los formatos binarios requieren bibliotecas de serialización o deserialización. La carga no se puede leer a menos que se descodifique.

Use un formato binario si desea reducir la superficie de conexión y transferir mensajes más rápido. Esta categoría de formato se recomienda en escenarios en los que el almacenamiento o el ancho de banda de red son un problema. Las opciones para formatos binarios incluyen Apache Avro, Búferes de protocolo de Google (protobuf), MessagePack y Representación concisa de objetos binarios (CBOR). Las ventajas y desventajas de esos formatos se describen más adelante en Opciones para codificar formatos.

La desventaja es que la carga no es legible por el usuario. La mayoría de los formatos binarios usan sistemas complejos que pueden ser costosos de mantener. Además, necesitan bibliotecas especializadas para descodificar, lo que podría no ser compatible si desea recuperar datos de archivo.

En el caso de los formatos nobinarios, el uso de un proceso de minificación para quitar espacios y caracteres técnicamente innecesarios, pero seguir manteniendo la alineación con la especificación del formato, puede ayudar con el tamaño de codificación. Evalúe las funcionalidades del codificador para que la minificación sea la predeterminada. Por ejemplo, JsonSerializerOptions.WriteIndented de .NET System.Text.Json.JsonSerializer controla la minificación automática al crear texto JSON.

Descripción de la carga

Una carga de mensaje llega como una secuencia de bytes. Para analizar esta secuencia, el consumidor debe tener acceso a los metadatos que describen los campos de datos de la carga. Hay dos enfoques principales para almacenar y distribuir metadatos:

Metadatos etiquetados. En algunas codificaciones, en particular JSON, los campos se etiquetan con el tipo de datos y el identificador, dentro del cuerpo del mensaje. Estos formatos son autodescriptivos porque se pueden convertir en un diccionario de valores sin hacer referencia a un esquema. Una manera de que el consumidor comprenda los campos es consultar los valores esperados. Por ejemplo, el productor envía una carga en JSON. El consumidor analiza el JSON en un diccionario y comprueba la existencia de campos para comprender la carga. Otra manera es que el consumidor aplique un modelo de datos compartido por el productor. Por ejemplo, si usas un lenguaje estáticamente tipado, muchas bibliotecas de serialización JSON pueden analizar una cadena JSON en una clase tipada.

Esquema. Un esquema define formalmente la estructura y los campos de datos de un mensaje. En este modelo, el productor y el consumidor tienen un contrato a través de un esquema bien definido. El esquema puede definir los tipos de datos, los campos obligatorios o opcionales, la información de versión y la estructura de la carga. El productor envía la carga útil según el esquema de escritura. El consumidor recibe la carga útil mediante la aplicación de un esquema de lectura. El mensaje se serializa o deserializa mediante las bibliotecas específicas de codificación. Hay dos maneras de distribuir esquemas:

  • Almacene el esquema como preámbulo o encabezado en el mensaje, pero independiente de la carga.

  • Almacene el esquema externamente.

Algunos formatos de codificación definen el esquema y usan herramientas que generan clases a partir del esquema. El productor y el consumidor usan esas clases y bibliotecas para serializar y deserializar la carga. Las bibliotecas también proporcionan comprobaciones de compatibilidad entre el esquema de escritura y lector. Tanto protobuf como Apache Avro siguen ese enfoque. La principal diferencia es que protobuf tiene una definición de esquema independiente del lenguaje, pero Avro usa JSON compacto. Otra diferencia es la manera en que ambos formatos proporcionan comprobaciones de compatibilidad entre los esquemas lector y escritor.

Otra manera de almacenar el esquema externamente en un registro de esquemas. El mensaje contiene una referencia al esquema y a la carga. El productor envía el identificador de esquema en el mensaje y el consumidor recupera el esquema especificando ese identificador de un almacén externo. Ambas partes usan una biblioteca del formato específico para la lectura y escritura de mensajes. Además de almacenar el esquema, un registro puede proporcionar comprobaciones de compatibilidad para asegurarse de que el contrato entre el productor y el consumidor no se interrumpe a medida que evoluciona el esquema.

Antes de elegir un enfoque, decida lo que es más importante: el tamaño de los datos de transferencia o la capacidad de analizar los datos archivados más adelante.

Almacenar el esquema junto con la carga produce un tamaño de codificación mayor y se prefiere para los mensajes intermitentes. Elija este enfoque si la transferencia de fragmentos más pequeños de bytes es fundamental o se espera una secuencia de registros. El costo de mantener un almacén de esquemas externo puede ser elevado.

Sin embargo, si la descodificación bajo demanda de la carga útil es más importante que el tamaño, incluyendo el esquema con la carga útil o el enfoque de metadatos etiquetados garantiza la descodificación posteriormente. Puede haber un aumento significativo en el tamaño del mensaje y afectar al costo del almacenamiento.

Versión del esquema

A medida que cambian los requisitos empresariales, se espera que cambie la forma y el esquema evolucionará. El control de versiones permite al productor indicar actualizaciones de esquema que podrían incluir nuevas características. Hay dos aspectos del control de versiones

  • El consumidor debe tener en cuenta los cambios.

    Una manera es que el consumidor compruebe todos los campos para determinar si el esquema ha cambiado. Otra manera es que el productor publique un número de versión de esquema junto con el mensaje. Cuando el esquema evoluciona, el productor incrementa la versión.

  • Los cambios no deben afectar ni interrumpir la lógica de negocios de los consumidores.

    Supongamos que se agrega un campo a un esquema existente. Si los consumidores que usan la nueva versión obtienen una carga útil conforme a la versión anterior, su lógica podría interrumpirse si no son capaces de pasar por alto la falta del nuevo campo. Teniendo en cuenta el caso inverso, supongamos que se quita un campo en el nuevo esquema. Es posible que los consumidores que usen el esquema antiguo no puedan leer los datos.

    Los formatos de codificación, como Avro, ofrecen la capacidad de definir valores predeterminados. En el ejemplo anterior, si el campo se agrega con un valor predeterminado, el campo que falta se rellena con el valor predeterminado. Otros formatos, como protobuf, proporcionan una funcionalidad similar a través de campos obligatorios y opcionales.

Estructura de la carga útil

Tenga en cuenta la forma en que los datos se organizan en la carga. ¿Es una secuencia de registros o una carga única discreta? La estructura de carga se puede clasificar en uno de estos modelos:

  • Matriz/diccionario/valor: define entradas que contienen valores en matrices unidimensionales o multidimensionales. Las entradas tienen pares clave-valor únicos. Se puede extender para representar las estructuras complejas. Algunos ejemplos incluyen, JSON, Apache Avro y MessagePack.

    Este diseño es adecuado si los mensajes están codificados individualmente con esquemas diferentes. Si tiene varios registros, la carga útil puede llegar a ser excesivamente redundante, lo que provoca un sobredimensionamiento de la carga.

  • Datos tabulares: la información se divide en filas y columnas. Cada columna indica un campo o el asunto de la información y cada fila contiene valores para esos campos. Este diseño es eficaz para un conjunto repetido de información, como los datos de serie temporal.

    CSV es uno de los formatos basados en texto más sencillos. Presenta datos como una secuencia de registros con un encabezado común. Para la codificación binaria, Apache Avro tiene un preámbulo similar a un encabezado CSV, pero genera un tamaño de codificación compacta.

Compatibilidad con bibliotecas

Debería usar formatos bien conocidos en lugar de un modelo propietario. Los formatos conocidos son compatibles mediante bibliotecas que la comunidad admite universalmente. Con formatos especializados, necesita bibliotecas específicas. Es posible que la lógica de negocios tenga que solucionar algunas de las opciones de diseño de API proporcionadas por las bibliotecas.

Para el formato basado en esquemas, elija una biblioteca de codificación que realice comprobaciones de compatibilidad entre el esquema lector y escritor. Algunas bibliotecas de codificación, como Apache Avro, esperan que el consumidor especifique el sistema de escritura y el esquema del lector antes de deserializar el mensaje. Esta comprobación garantiza que el consumidor conozca las versiones del esquema.

Interoperabilidad

La elección de formatos puede depender de la carga de trabajo o del ecosistema tecnológico concretos.

Por ejemplo:

  • Azure Stream Analytics tiene compatibilidad nativa con JSON, CSV y Avro. Cuando la carga de trabajo usa Stream Analytics, tiene sentido elegir uno de estos formatos.

  • JSON es un formato de intercambio estándar para las API REST HTTP. Si la aplicación recibe cargas JSON de los clientes y, a continuación, las coloca en una cola de mensajes para el procesamiento asincrónico, podría tener sentido usar JSON para la mensajería, en lugar de volver a codificar en un formato diferente.

Estos son solo dos ejemplos de consideraciones de interoperabilidad. En general, los formatos estandarizados son más interoperables que los formatos personalizados. En las opciones basadas en texto, JSON es uno de los más interoperables.

Opciones para formatos de codificación

Estos son algunos formatos de codificación populares. Tenga en cuenta las consideraciones antes de elegir un formato.

JSON

JSON es un estándar abierto (IETF RFC8259). Es un formato basado en texto que sigue el modelo array/dictionary/value.

JSON se puede usar para etiquetar metadatos y puede analizar la carga sin un esquema. JSON admite la opción de especificar campos opcionales, lo que ayuda con la compatibilidad con versiones futuras y pasadas.

La mayor ventaja es que está disponible universalmente. Es más interoperable y el formato de codificación predeterminado para muchos servicios de mensajería.

Al ser un formato basado en texto, no es eficaz a través de la red y no es una opción ideal cuando el almacenamiento es una preocupación. Use técnicas de minificación siempre que sea posible. Si va a devolver elementos almacenados en caché directamente a un cliente a través de HTTP, el almacenamiento de JSON podría ahorrar el costo de deserializar desde otro formato y, a continuación, serializar en JSON.

Use JSON para los mensajes de registro único o para una secuencia de mensajes en los que cada mensaje tiene un esquema diferente. Evite usar JSON para una secuencia de registros, como para los datos de serie temporal.

Hay otras variaciones de JSON, como JSON binario (BSON), que es una codificación binaria alineada para trabajar con MongoDB.

Valores separados por comas (CSV)

CSV es un formato tabular basado en texto. El encabezado de la tabla indica los campos. Es una opción preferida en la que el mensaje contiene un conjunto de registros.

La desventaja es la falta de estandarización. Hay muchas maneras de expresar separadores, encabezados y campos vacíos.

Búferes de protocolo (protobuf)

Búferes de protocolo (o protobuf) es un formato de serialización que utiliza archivos de definición fuertemente tipados para definir esquemas en pares clave-valor. A continuación, estos archivos de definición se compilan en clases específicas del lenguaje que se usan para serializar y deserializar mensajes.

El mensaje contiene una carga binaria pequeña comprimida, que resulta una transferencia más rápida. La desventaja es que la carga no es comprensible para los humanos. Además, dado que el esquema es externo, no se recomienda en los casos en los que tenga que recuperar datos archivados.

Apache Avro

Apache Avro es un formato de serialización binaria que usa un archivo de definición similar a protobuf, pero no hay un paso de compilación. En su lugar, los datos serializados siempre incluyen un preámbulo de esquema.

El preámbulo puede contener el encabezado o un identificador de esquema. Debido al tamaño de codificación más pequeño, se recomienda Avro para los datos de streaming. Además, dado que tiene un encabezado que se aplica a un conjunto de registros, es una buena opción para los datos tabulares.

Apache Parquet

Apache Parquet es un formato de archivo de almacenamiento en columnas asociado normalmente a Apache Hadoop y a marcos de procesamiento de datos relacionados.

El formato admite la compresión de datos y tiene algunas funcionalidades limitadas para admitir la evolución del esquema. Este formato es un ejemplo de uno que probablemente solo utilizarías debido a otras opciones de tecnologías de macrodatos en tu carga de trabajo que serán responsables de la creación o el consumo de estos datos.

MessagePack

MessagePack es un formato de serialización binaria diseñado para ser compacto para la transmisión por cable. No hay esquemas de mensaje ni comprobación de tipos de mensaje. Este formato no se recomienda para el almacenamiento masivo.

CBOR

representación de objeto binario conciso (CBOR) (especificación) es un formato binario que ofrece un tamaño de codificación pequeño. La ventaja de CBOR sobre MessagePack es que es compatible con IETF en RFC7049.

Pasos siguientes