Mostrando las entradas con la etiqueta Apache Avro. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Apache Avro. Mostrar todas las entradas

domingo, 19 de septiembre de 2021

Primeros pasos con Apache Kafka parte 13



Seguimos con Kafka. 

Los archivos Avro deben almacenar el esquema completo en el archivo de datos que están asociados, almacenar el esquema completo en cada registro generalmente será más del doble del tamaño del registro. Para resolver este problema podemos utilizar un registro de esquemas. Los Schema Registry no forma parte de Apache Kafka, pero hay varias opciones de código abierto para elegir. Usaremos Confluent Schema Registry para este ejemplo. 

La idea es almacenar todos los esquemas utilizados luego, simplemente almacenamos el identificador del esquema en el registro que producimos en Kafka. Los consumidores pueden usar el identificador para extraer el registro del registro de esquema y deserializar los datos. 

A continuación, se muestra un ejemplo de cómo producir objetos Avro generados en Kafka:

Properties props = new Properties();

props.put("bootstrap.servers", "localhost:9092");

props.put("key.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");

props.put("value.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");

props.put("schema.registry.url", schemaUrl);

String topic = "customerContacts";

int wait = 500;

Producer<String, Customer> producer = new KafkaProducer<String,Customer>(props);

while (true) {

    Customer customer = CustomerGenerator.getNext();

    System.out.println("Generated customer " + customer.toString());

    ProducerRecord<String, Customer> record = new ProducerRecord<>(topic, customer.getId(), customer);

    producer.send(record);

}

Usamos KafkaAvroSerializer para serializar nuestros objetos con Avro. AvroSerializer también puede manejar primitivas, por lo que luego podemos usar String como clave de registro y nuestro objeto Customer como valor.

schema.registry.url es un nuevo parámetro. Esto simplemente apunta a dónde almacenamos los esquemas.

El cliente es nuestro objeto generado. Le decimos al productor que nuestros registros contendrán Cliente como valor.

También creamos una instancia de ProducerRecord con Customer como el tipo de valor y pasamos un objeto Customer al crear el nuevo registro.

Eso es todo. Enviamos el registro con nuestro objeto Cliente y KafkaAvroSerializer se encargará del resto.

¿Qué sucede si prefiere utilizar objetos Avro genéricos en lugar de los objetos Avro generados?
No hay problema. En este caso, solo necesita proporcionar el esquema:

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
props.put("value.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
props.put("schema.registry.url", url);

String schemaString = "{\"namespace\": \"customerManagement.avro\",
\"type\": \"record\", " +
"\"name\": \"Customer\"," +
"\"fields\": [" +
"{\"name\": \"id\", \"type\": \"int\"}," +
"{\"name\": \"name\", \"type\": \"string\"}," +
"{\"name\": \"email\", \"type\": [\"null\",\"string
\"], \"default\":\"null\" }" +
"]}";

Producer<String, GenericRecord> producer = new KafkaProducer<String, GenericRecord>(props);
Schema.Parser parser = new Schema.Parser();
Schema schema = parser.parse(schemaString);

for (int nCustomers = 0; nCustomers < customers; nCustomers++) {
    String name = "exampleCustomer" + nCustomers;
    String email = "example " + nCustomers + "@example.com"
    GenericRecord customer = new GenericData.Record(schema);
    customer.put("id", nCustomer);
    customer.put("name", name);
    customer.put("email", email);
    ProducerRecord<String, GenericRecord> data = new ProducerRecord<String,
    GenericRecord>("customerContacts", name, customer);
    producer.send(data);
}

Seguimos usando el mismo KafkaAvroSerializer.

Y proporcionamos el URI del mismo registro de esquema.

Pero ahora también necesitamos proporcionar el esquema Avro, ya que no lo proporciona el objeto generado por Avro.

Nuestro tipo de objeto es un Avro GenericRecord, que inicializamos con nuestro esquema y los datos que queremos escribir.

Entonces, el valor de ProducerRecord es simplemente un GenericRecord que cuenta nuestro esquema y datos. El serializador sabrá cómo obtener el esquema de este registro, almacenarlo en el registro de esquema y serializar los datos del objeto.


viernes, 17 de septiembre de 2021

Primeros pasos con Apache Kafka parte 12


Seguimos con Kafka. 

Apache Avro es un formato de serialización de datos independiente del lenguaje. El proyecto fue creado por Doug Cutting para proporcionar una forma de compartir archivos de datos con una gran audiencia.

Los datos de Avro se describen en un esquema independiente del lenguaje. El esquema generalmente se describe en JSON y la serialización suele ser en archivos binarios, aunque también se admite la serialización en JSON. Avro asume que el esquema está presente al leer y escribir archivos, generalmente incrustando el esquema en los propios archivos.




Una de las características más interesantes de Avro, y lo que lo hace adecuado para su uso en sistemas de mensajería como Kafka, es que cuando la aplicación que está escribiendo mensajes cambia a un nuevo esquema, las aplicaciones que leen los datos pueden continuar procesando mensajes sin necesidad de cambiar o actualizar.

Supongamos que el esquema original fuera:

{

     "namespace": "customerManagement.avro",

     "type": "record",

     "name": "Customer",

     "fields": [

          {"name": "id", "type": "int"},

          {"name": "name", "type": "string""},

          {"name": "faxNumber", "type": ["null", "string"], "default": "null"}

     ]

}

Usamos este esquema durante unos meses y generamos algunos terabytes de datos en este formato. Ahora suponga que decidimos que en la nueva versión, actualizaremos al siglo XXI y ya no incluiremos un campo de número de fax y en su lugar usaremos un campo de correo electrónico.

El nuevo esquema sería:

{"namespace": "customerManagement.avro",
    "type": "record",
    "name": "Customer",
    "fields": [
        {"name": "id", "type": "int"},
        {"name": "name", "type": "string"},
        {"name": "email", "type": ["null", "string"], "default": "null"}
    ]
}

Ahora, después de actualizar a la nueva versión, los registros antiguos contendrán "faxNumber" y los registros nuevos contendrán "email". En muchas organizaciones, las actualizaciones se realizan lentamente y durante muchos meses. Por lo tanto, debemos considerar cómo las aplicaciones anteriores a la actualización que aún usan los números de fax y las aplicaciones posteriores a la actualización que usan el correo electrónico podrán manejar todos los eventos en Kafka.

La aplicación de lectura contendrá llamadas a métodos similares a getName (), getId () y getFaxNumber. Si encuentra un mensaje escrito con el nuevo esquema, getName() y getId () continuará funcionando sin modificaciones, pero getFaxNumber () devolverá nulo porque el mensaje no contendrá un número de fax.

Ahora suponga que actualizamos nuestra aplicación de lectura y ya no tiene el método getFaxNumber() sino getEmail(). Si encuentra un mensaje escrito con el esquema anterior, getEmail() devolverá un valor nulo porque los mensajes anteriores no contienen una dirección de correo electrónico.

Este ejemplo ilustra el beneficio de usar Avro: aunque cambiemos el esquema en los mensajes sin cambiar todas las aplicaciones que leen los datos, no habrá excepciones ni errores de ruptura y no será necesario realizar costosas actualizaciones de los datos existentes.

Sin embargo, hay dos advertencias para este escenario:
  • El esquema utilizado para escribir los datos y el esquema esperado por la aplicación de lectura deben ser compatibles. La documentación de Avro incluye reglas de compatibilidad.
  • El deserializador necesitará acceder al esquema que se utilizó al escribir los datos, incluso cuando sea diferente del esquema esperado por la aplicación que accede a los datos. En los archivos Avro, el esquema de escritura se incluye en el propio archivo, pero hay una mejor manera de manejar esto para los mensajes de Kafka. Que veremos en próximos post...

sábado, 31 de julio de 2021

Primeros pasos con Apache Kafka parte 2


Seguimos con Kafka.

Si bien los mensajes son matrices de bytes opacas para el propio Kafka, se recomienda que se imponga una estructura o esquema adicional en el contenido del mensaje para que se pueda entender fácilmente. Hay muchas opciones disponibles para el esquema de mensajes, según las necesidades individuales de su aplicación. Los sistemas simplistas, como Javascript Object Notation (JSON) y Extensible Markup Language (XML), son fáciles de usar y legibles por humanos. Sin embargo, carecen de características como el manejo de tipos robusto y la compatibilidad entre las versiones del esquema. Muchos desarrolladores de Kafka favorecen el uso de Apache Avro, que es un marco de serialización desarrollado originalmente para Hadoop. Avro proporciona un formato de serialización compacto; esquemas que están separados de las cargas útiles del mensaje y que no requieren que se genere código cuando cambian; y datos sólidos tipado y evolución del esquema, con compatibilidad tanto hacia atrás como hacia adelante.

Un formato de datos coherente es importante en Kafka, ya que permite desacoplar la escritura y la lectura de mensajes. Cuando estas tareas están estrechamente vinculadas, las aplicaciones que se suscriben a los mensajes deben actualizarse para manejar el nuevo formato de datos, en paralelo con el formato anterior. Solo entonces se podrán actualizar las aplicaciones que publican los mensajes para utilizar el nuevo formato. Al utilizar esquemas bien definidos y almacenarlos en un repositorio común, los mensajes en Kafka se puedan entender sin coordinación.

Los mensajes de Kafka se clasifican en temas. Las analogías más cercanas para un tema son una tabla de base de datos o una carpeta en un sistema de archivos. Además, los temas se desglosan en un número de particiones. Volviendo a la descripción del "registro de confirmación", una partición es un registro único. Los mensajes se escriben en él de forma de solo anexo y se leen en orden de principio a fin. Tenga en cuenta que, dado que un tema suele tener varias particiones, no hay garantía de que los mensajes se ordenen por tiempo en todo el tema, solo dentro de una única partición. 

Las particiones también son la forma en que Kafka proporciona redundancia y escalabilidad. Cada partición se puede alojar en un servidor diferente, lo que significa que un solo tema se puede escalar horizontalmente en varios servidores para proporcionar un rendimiento mucho más allá de la capacidad de un solo servidor.

El término stream se usa a menudo cuando se habla de datos dentro de sistemas como Kafka. La mayoría de las veces, se considera que una secuencia es un solo tema de datos, independientemente del número de particiones. Esto representa un único flujo de datos que se mueve de los productores a los consumidores. Esta forma de referirse a los mensajes es más común cuando se habla de procesamiento de flujo, que es cuando los frameworks (algunos de los cuales son Kafka Streams, Apache Samza y Storm) operan en los mensajes en tiempo real. Este método de operación se puede comparar con la forma en que los frameworks offline, a saber, Hadoop, están diseñados para trabajar con datos masivos en un momento posterior. 


jueves, 17 de mayo de 2018

Apache Avro


Apache Avro es un sistema de serialización de datos.

Avro proporciona:
  • Estructuras de datos ricas
  • Un formato de datos binario compacto, rápido.
  • Un archivo contenedor, para almacenar datos persistentes.
  • Llamada a procedimiento remoto (RPC).
  • Integración simple con lenguajes dinámicos. No se requiere la generación de código para leer o escribir archivos de datos ni para usar o implementar protocolos RPC. 
Avro se basa en esquemas. Cuando se leen datos Avro, el esquema utilizado al escribirlo siempre está presente. Esto permite que cada dato se escriba sin gastos generales por valor, lo que hace que la serialización sea rápida y pequeña. Esto también facilita el uso con lenguajes dinámicos de scripting, ya que los datos, junto con su esquema, son completamente autodescriptivos.
Cuando los datos de Avro se almacenan en un archivo, su esquema se almacena con él, de modo que los archivos pueden ser procesados posteriormente por cualquier programa. Si el programa que lee los datos espera un esquema diferente, esto se puede resolver fácilmente, ya que ambos esquemas están presentes.

Cuando se usa Avro en RPC, el cliente y el servidor intercambian esquemas en el enlace de conexión. (Esto se puede optimizar para que, en la mayoría de las llamadas, no se transmitan realmente esquemas.) Dado que tanto el cliente como el servidor tienen el esquema completo del otro, la correspondencia entre los mismos campos con nombre, campos faltantes, campos adicionales, etc. puede resolverse fácilmente .

Los esquemas Avro se definen con JSON. Esto facilita la implementación en Lenguajes que ya tienen bibliotecas JSON. Avro proporciona una funcionalidad similar a sistemas como Thrift, Protocol Buffers, etc. Avro difiere de estos sistemas en los siguientes aspectos fundamentales.

  • Tipado dinámico: Avro no requiere que se genere ese código. Los datos siempre van acompañados de un esquema que permite el procesamiento completo de esos datos sin generación de códigos, tipos de datos estáticos, etc. Esto facilita la construcción de sistemas e lenguajes genéricos de procesamiento de datos.
  • Datos no etiquetados: dado que el esquema está presente cuando se leen los datos, es necesario codificar considerablemente menos información de tipo con los datos, lo que da como resultado un tamaño de serialización más pequeño.

No hay identificadores de campo asignados manualmente: cuando un esquema cambia, tanto el esquema antiguo como el nuevo siempre están presentes cuando se procesan los datos, por lo que las diferencias se pueden resolver simbólicamente, usando los nombres de los campos.