Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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 (o campos). Decida las características de los valores de esos campos. Tenga en cuenta las siguientes preguntas.
- ¿Cuál es el tipo de datos más eficaz?
- ¿La carga siempre tiene campos específicos?
- ¿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 específicos 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. A continuación, elija un formato de codificación que satisfaga sus necesidades.
El consumidor debe comprender esas decisiones para leer correctamente 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 acceder a los datos. Este proceso garantiza que ambas entidades compartan el mismo modelo. Siempre que la forma permanezca sin cambios, 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 suelen producir mensajes más grandes. Es posible que otros formatos no analicen los datos tan fácilmente, pero dan lugar a mensajes más compactos. En este artículo se describen los factores clave que le ayudarán a elegir el formato correcto.
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 elección del formato. Los mensajes relacionados con las transacciones empresariales probablemente contienen datos altamente estructurados. Además, es posible que quiera recuperar los datos estructurados 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.
Tenga en cuenta los siguientes factores 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, por lo que una persona puede inspeccionarla sin usar bibliotecas de código. Este enfoque facilita la lectura y comprensión de los datos. Los formatos legibles son adecuados para los datos de archivo. Dado que un usuario puede leer la carga, los formatos basados en texto son más fáciles de depurar y enviar a registros para solucionar errores.
La desventaja de la codificación basada en texto es que la carga tiende a ser mayor. El tamaño de la carga a menudo puede reducirse mediante un proceso de minificación, siempre que pueda revertirse para garantizar 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. Como alternativa, si solo es necesario cifrar campos específicos y prefiere reducir los costos en la nube, considere la posibilidad de usar una biblioteca como NServiceBus.
Tamaño de codificación
El tamaño del mensaje afecta al rendimiento de entrada y salida 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 y deserialización. La carga solo se puede leer cuando se descodifica.
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 estos formatos se describen más adelante en Opciones para los formatos de codificación.
La desventaja del formato binario 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, un proceso de minificación quita espacios y caracteres innecesarios, a la vez que conserva el cumplimiento de la especificación del formato. Este enfoque ayuda a reducir el tamaño de codificación sin modificar la estructura. 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. Los dos enfoques principales para almacenar y distribuir metadatos son:
Metadatos etiquetados. En algunos formatos de codificación, 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 que comparte el productor. Por ejemplo, si utiliza un lenguaje con tipos estáticos, muchas de las bibliotecas de serialización JSON pueden analizar una cadena JSON en una clase tipificada.
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 de acuerdo con el esquema del escritor. El consumidor recibe la carga útil mediante la aplicación de un esquema de lectura. El mensaje se serializa y deserializa mediante las bibliotecas específicas de codificación. Los esquemas se pueden distribuir de dos maneras:
Almacene el esquema como preámbulo o encabezado en el mensaje, pero por separado 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 diferencia clave es que protobuf tiene una definición de esquema independiente del lenguaje y 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 es 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. El consumidor recupera el esquema especificando ese identificador de un almacén externo. Ambas partes usan una biblioteca de formato específica para leer y escribir 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 si el tamaño de los datos de transferencia o la capacidad de analizar los datos archivados más adelante es más importante.
Almacenar el esquema junto con la carga genera un tamaño de codificación mayor y es ideal para 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 que afecta al costo del almacenamiento.
Versionado de esquemas
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. El control de versiones tiene dos aspectos clave:
El consumidor debe realizar un seguimiento y comprender 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 utilizan la nueva versión reciben una carga útil según la versión antigua, su lógica podría romperse si no pueden pasar por alto la falta del nuevo campo. Ahora considere el escenario opuesto. Si 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 proporcionan 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
Considere si los datos de la carga se estructuran como una secuencia de registros o como una sola carga discreta. La estructura de carga se puede clasificar en uno de los modelos siguientes:
Array/dictionary/value: Define entradas que contienen valores en una o matrices multidimensionales. Las entradas tienen pares clave-valor únicos. El modelo se puede extender para representar estructuras complejas. Algunos ejemplos incluyen JSON, Apache Avro y MessagePack.
Este diseño es adecuado si los mensajes se codifican individualmente con esquemas diferentes. Si tiene varios registros, la carga puede volverse excesivamente redundante. Esta redundancia puede hacer que la carga se sobredimensione.
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.
Comma-Separated Valores (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 que genera un tamaño de codificación más compacto.
Compatibilidad con bibliotecas
Debe usar formatos conocidos en lugar de un modelo propietario. Los formatos conocidos son compatibles a través de bibliotecas que cuentan con el respaldo universal de la comunidad. 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 un formato basado en esquemas, elija una biblioteca de codificación que realice comprobaciones de compatibilidad entre el esquema de lector y escritor. Las bibliotecas de codificación específicas, como Apache Avro, esperan que el consumidor especifique tanto el sistema de escritura como el esquema del lector antes de deserializar el mensaje. Esta comprobación garantiza que el consumidor conozca las versiones del esquema.
Interoperabilidad
Su elección de formatos puede depender de la carga de trabajo o el ecosistema de tecnología específicos.
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, puede 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. Los formatos estandarizados suelen ser 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
Los siguientes formatos de codificación populares se usan para la representación y transmisión de datos. Tenga en cuenta las consideraciones antes de elegir un formato.
JSON
JSON es un estándar abierto, con su formato definido por el Grupo de tareas de ingeniería de Internet (IETF) en RFC 8259. JSON 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 anteriores y posteriores.
La mayor ventaja es que está disponible universalmente. JSON es el formato de codificación más interoperable y el valor predeterminado para muchos servicios de mensajería.
Dado que JSON es un formato basado en texto, no es eficiente a través de la red y no es ideal cuando el almacenamiento es un problema. Use técnicas de minificación siempre que sea posible. Si devuelve 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). BSON es una codificación binaria alineada para trabajar con MongoDB.
CSV
CSV es un formato tabular basado en texto. El encabezado de la tabla indica los campos. CSV es adecuado para los mensajes que contienen un conjunto de registros.
La desventaja de CSV es una falta de normalización. Hay varias maneras de expresar separadores, encabezados y campos vacíos.
Búferes de protocolo
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 y comprimida, lo que da como resultado una transferencia de datos más rápida. La desventaja es que la carga útil no es legible para humanos. Además, dado que el esquema se almacena externamente, este formato no es ideal para escenarios que requieren que recupere datos archivados.
Apache Avro
Apache Avro es un formato de serialización binario que usa un archivo de definición similar a protobuf, pero sin 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 a su 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 adecuado 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.
Apache Parquet admite la compresión de datos y tiene funcionalidades limitadas para la evolución del esquema. Este formato se usa normalmente cuando otras tecnologías de macrodatos de la carga de trabajo lo requieren para la creación o el consumo de datos.
MessagePack
MessagePack es un formato de serialización binario diseñado para ser compacto para la transmisión a través de la conexión. MessagePack carece de definición de esquema y comprobación de tipos. Este formato no se recomienda para el almacenamiento masivo.
CBOR
CBOR (Especificación) es un formato binario que proporciona un tamaño de codificación pequeño. La ventaja de usar CBOR sobre MessagePack es su cumplimiento con IETF en RFC7049.