Kafka con Spring Boot Parte 2: Exactly Once
En este nuevo artículo de Kafka con Spring Boot Parte 2: Exactly Once, vamos a ver como conseguimos implementar una entrega exactly once entre productor y consumidor a través del API Transaccional de Kafka.
¿Tipos de entrega de mensajes en Kafka?
En kafka se tienen diferentes tipos de entrega:
- At most once-messages: El productor no reenvía el mensaje cuando el ACK devuelve un error, o el tiempo de espera del ACK se ha agotado, el problema de esta forma de envío es que el mensaje podría terminar llegar a no enviarse al topic y por tanto nunca se entregaría al consumidor.
- At least once-messages: En este tipo de envío el productor intenta reenviar el mensaje cuando se tuvo un error o el ACK dio time out, ya que se considera que el mensaje no se escribió en el topic de kafka.
- Exactly once: En este caso en el que nos centraremos en esta entrada, si el productor intenta reenviar un mensaje, se asegura siempre que el mensaje es entregado una y sola una vez al consumidor final.
Cuando implementamos Exactly Once en nuestra aplicación, lo que estamos persiguiendo es que cada mensaje sea entregado una única vez, da igual las veces que se reintente entregar el mensaje, este solo será consumido una vez.
Configuración de Kafka en Spring Boot para envíar mensajes con Exactly Once
Añadir dependencias Maven
Sin duda lo más fácil para empezar integrar kafka a una aplicación Spring Boot, es empezar con el proyecto initializr de Spring e indicar las dependencias a usar.
<dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency>
Dependencia principal para el proyecto
<dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka-test</artifactId> <scope>test</scope> </dependency>
La dependencia anterior nos vendrá bien para realizar nuestros test integrados.
Activando transaccionalidad en el Productor
Nos debemos asegurar que cada mensaje llegue exactamente una vez independientemente del número de veces que el productor pueda llegar a enviar ese mensaje. Para ello, tenemos que realizar una serie de configuraciones en nuestra aplicación Spring Boot, en nuestro fichero application.properties o application.yaml
enable.idempotence: true transactional.id: prod-A transaction-id-prefix: transaction
Al activar la idempotencia, kafka va a utilizar el identificador de esta transacción (transactional.id: prod-A) como parte de su algoritmo para evitar los mensajes duplicados que el productor envíe asegurando así la idempontencia. Cada identificación de la transacción tiene que ser distinta para cada productor.
Una vez establecidas las propiedades tenemos que envíar los mensajes dentro de una transacción, para ello vamos a hacer uso de la clase KafkaTemplate y utilizar el método executeInTransaction.
kafkaTemplate.executeInTransaction(kt -> { var result = kt.send(event); result.addCallback( successResult -> log.debug("Message {} sent ok with result: {}", getEventAsString(event), successResult), exception -> { log.error("Non retriable exception found in send operation: {}", exception.getClass().getName()); }); return result; });
Al utilizar el fragmento de código anterior, hace que el broker reconozca que vamos a enviar cada mensaje dentro de una transacción que estará identificado por su transactional.id y un número de secuencia o un identificador de tiempo.
Activando transacción en el Consumidor
Cuando leemos los mensajes de un broker, se hace en orden de llegada, pero lo que vamos a hacer es indicar al broker que vamos a esperar a leer los mensajes transaccionales hasta que se tenga confirmación de la transacción, para ello se lo indicamos en el fichero application.properties o application.yaml de la siguiente manera:
properties: isolation.level: read_committed
Por defecto es read_uncommited, es decir, lee todos los mensajes del offset en order sin esperar al commit de la transacción.
Conclusión
En esta entrada hemos visto como aplicar Kafka con Spring Boot mediante exactly once. Gracias a lo cual nos aseguramos tener transaccionalidad y asegurarnos que el mensaje llegue una y sola una vez al consumidor.
Otros artículos que te pueden interesar:
Kafka en una Arquitectura de Microservicios
Kafka con Spring Boot Parte Uno
Coreografía vs Orquestación en Microservicios