Kafka - Buenas prácticas
Manejo de hilos para incrementar la velocidad de lectura
En escenarios de un alto número de mensajes nos podríamos preguntar:
¿Será necesario crear varias instancias de consumidor en un mismo proceso para aumentar la velocidad de lectura?
La realidad es que no es necesario, deberíamos evitar crear múltiples consumidores en un mismo proceso por tres simples motivos:
-
Cada cliente mantendrá una conexión con todos los brokers en el clúster.
-
Si se crean múltiples clientes los datos no se leerán de forma eficiente en comparación que cuando se crea uno solo.
-
Todas las operaciones del cliente son seguras para subprocesos por lo tanto con uno es suficiente.
Recordemos que cada partición solo puede tener un cliente conectado de un mismo grupo de consumo. Esto significa que si creamos varias instancias en un mismo proceso, vamos a limitar el crecimiento del número de consumidores en cantidad de máquinas y nuestro worker se volvería un cuello de botella.
En estos casos lo que debemos hacer es suscribirnos al tópico o a los tópicos y leer los datos para luego entregárselos a un pool de tareas que las ejecuten eficientemente.
En el caso de la librería de .Net, el método “Poll” lo podríamos llamar desde varias tareas para incrementar la velocidad de lectura.
En el caso de Java, la librería realiza la lectura por lotes y no es necesario crear varias tareas para realizar el consumo.
Si queremos incrementar el performance más allá de lo que puede procesar una sola máquina, podemos desplegar nuestra aplicación en múltiples máquinas para que cada una se conecte a una partición específica.
A continuación mostramos un ejemplo de cómo sería la implementación de un consumidor con la librería de C#. En él podemos evidenciar cómo se crean varias tareas para realizar el poll sobre el consumidor. El delegado onMessage se ejecutará bajo el contexto de cada tarea y de esa forma podemos incrementar la velocidad de lectura de Kafka.

Garantizar el procesamiento de los mensajes sin pérdida de datos
Existen casos donde se vuelve necesario garantizar que los mensajes que se lean sean procesados en su totalidad ya que por requerimientos de negocio no podemos permitirnos perderlos.
Bajo este escenario, se podría realizar commit manual para poder tener control del momento en el que se realiza el mismo. La idea no es irse al extremo de hacer commit por cada mensaje que se lea, por lo menos no para tópicos de alto volumen de mensajes, ya que esto atentaría contra el performance.
En estos casos podemos implementar una estrategia de commit por lotes, es decir que lo haremos cada n mensajes procesados, donde n es la cantidad de mensajes leídos antes de cada commit.
En la implementación de nuestro consumidor vamos a realizar el commit luego de que nos aseguremos de que todos los mensajes leídos con anterioridad hayan sido procesados. En caso de una falla en el procesamiento de uno de los mensajes podemos reintentarlo y si la falla persiste, podemos guardar el mensaje en un storage externo como una cola o un archivo y continuar el procesamiento del resto
Para los mensajes que se guardaron en el repositorio externo podemos tener otra tarea que lo siga reintentando y notifique en caso de no poder procesarlo.
De esta forma garantizamos que todos los mensajes han sido procesados y no hay pérdida de datos.
¿Cómo definir el número de particiones necesarias?
En los casos en que necesitemos tópicos con más de una partición podemos usar el siguiente procedimiento para identificar la cantidad de particiones requeridas.
A continuación vamos a mostrar un método tomado de la propuesta de Confluent [Jun Rao, 2015, Confluent.io] que se basa en el throughput:
- Lo primero que tenemos que hacer es identificar el throughput que requerimos y este lo llamaremos t.
- Luego vamos a medir el throughtput que podemos alcanzar en una sola partición para la producción (lo llamaremos p) y el throughput de lectura (lo llamaremos c).
- Con estos datos podemos decir que en número de particiones necesitamos es al menos n=max(t/p , t/c)
Hay que tener en cuenta que el throughput por partición depende de las cofiguraciones como la comprensión, el tipo de ack, el factor del replica etc. [Jun Rao, 2015, Confluent.io]
Ejemplo productor y consumidor en Java
Ejemplos productores
- Confluent Example - Simple Producer
- Confluent Example - Specific Avro Producer
- Kafka Org Example - Simple Producer
- Cloudurable Example - Advanced Producer
Ejemplos consumidores
- Confluent Example - Simple Consumer
- Confluent Example - Specific Avro Consumer
- Kafka Org Example - Simple Consumer
- Cloudurable Example - Advanced Consumer
Kafka - Comandos
| Titulo | Comando |
|---|---|
| Crear Tópico | |
| Eliminar Grupos de Consumo Zookeeper | ./kafka-consumer-groups.sh --zookeeper --delete --group |