Todos los productos
Search
Centro de documentos

ApsaraMQ for Kafka:Mejores prácticas para productores

Última actualización: Sep 04, 2026

Cuando un productor de Kafka envía grandes volúmenes de mensajes, una configuración incorrecta de reintentos, lotes o confirmaciones puede provocar pérdida de datos, latencia excesiva o errores de memoria insuficiente. Este tema explica cómo configurar su productor de ApsaraMQ for Kafka para garantizar envíos confiables y un throughput óptimo. Todos los ejemplos usan el cliente Java. Los conceptos fundamentales aplican a otros lenguajes, aunque los nombres de los parámetros y los detalles de implementación pueden variar.

Envío de un mensaje

Toda interacción del productor comienza con producer.send(), que acepta un objeto ProducerRecord con el topic, la partición, la marca de tiempo, la clave y el valor.

Future<RecordMetadata> metadataFuture = producer.send(new ProducerRecord<String, String>(
        topic,   // The message topic.
        null,   // The partition number. Set this to null to let the producer assign a partition.
        System.currentTimeMillis(),   // The timestamp.
        String.valueOf(value.hashCode()),   // The message key.
        value   // The message value.
));

El método send() es asíncrono. Para bloquear la ejecución hasta obtener el resultado, llame a:

RecordMetadata metadata = metadataFuture.get(timeout, TimeUnit.MILLISECONDS);

Para consultar ejemplos completos del SDK, consulte SDK descripción general.

Clave y valor

Los mensajes en ApsaraMQ for Kafka 0.10.2 tienen dos campos:

  • Clave: identificador del mensaje. Establezca una clave única por mensaje para rastrear su ciclo de vida en los registros de envío y consumo.

  • Valor: contenido del mensaje.

Para envíos de alto volumen sin enrutamiento basado en claves, omita la clave y use la sticky partitioning strategy. Esto mejora la eficiencia del procesamiento por lotes.

Importante

ApsaraMQ for Kafka 0.11.0 y versiones posteriores admiten encabezados. Para usarlos, actualice el servidor a la versión 2.2.0.

Seguridad de hilos

Una instancia de productor es segura para hilos y puede enviar mensajes a cualquier topic. Use un solo productor por aplicación.

Configuración de reintentos

En un entorno distribuido, los envíos pueden fallar debido a problemas de red. El error puede ocurrir en dos puntos: el mensaje llegó al broker pero se perdió la confirmación (ACK), o bien el mensaje nunca llegó al broker.

ApsaraMQ for Kafka usa una arquitectura de red de dirección IP virtual (VIP) que cierra automáticamente las conexiones inactivas. Los clientes inactivos suelen recibir un error de connection reset by peer. Los reintentos permiten manejar estos fallos transitorios.

Parámetro

Descripción

Valor recomendado

retries

Número máximo de intentos de reintento para un envío fallido.

Use el valor predeterminado (definido por la versión del cliente).

retry.backoff.ms

Retraso entre reintentos, en milisegundos.

1000

Confirmaciones

El parámetro acks controla cuántas réplicas deben confirmar una escritura antes de que el broker responda. Elija una configuración según sus requisitos de durabilidad y rendimiento.

Configuración

Comportamiento

Throughput

Durabilidad

acks=0

No se requiere respuesta del broker.

Máximo

Mínima: es probable que haya pérdida de datos ante cualquier fallo

acks=1

Respuesta después de que el nodo principal escriba los datos.

Medio

Media: hay pérdida de datos si el nodo principal falla antes de la replicación

acks=all

Respuesta después de que el nodo principal y todos los nodos de réplica sincronizados (ISR) escriban los datos.

Mínimo

Máxima: solo hay pérdida de datos si los nodos principal y de réplica fallan simultáneamente

Para mejorar el rendimiento de envío, establezca acks=1.

Optimización del procesamiento por lotes

El productor agrupa los mensajes destinados a la misma partición en lotes antes de enviarlos. Los lotes más grandes reducen las solicitudes de red, disminuyen el uso de CPU y mejoran el throughput y la latencia. Los lotes pequeños provocan colas de solicitudes tanto en el cliente como en el servidor.

Dos parámetros controlan el procesamiento por lotes:

Parámetro

Descripción

Valor predeterminado

Recomendación

batch.size

Tamaño máximo del lote por partición, en bytes. Se activa una solicitud de red cuando el lote alcanza este tamaño.

16384 (16 KB)

Mantenga el valor predeterminado de 16384. Un valor demasiado bajo degrada el rendimiento y la estabilidad.

linger.ms

Tiempo máximo que un mensaje espera en el búfer antes de enviarse, en milisegundos. Cuando expira este tiempo, el productor envía el lote independientemente de batch.size.

0

Establezca un valor entre 100 y 1000.

Un lote se envía cuando se alcanza cualquiera de los umbrales, lo que ocurra primero. Para lograr un buen equilibrio entre throughput y latencia, establezca batch.size=16384 y linger.ms=1000.

Use un cliente de la versión 2.4 o posterior, que habilita la sticky partitioning strategy de forma predeterminada para reducir aún más los envíos fragmentados.

Estrategia de particionamiento adhesivo

Solo los mensajes enviados a la misma partición se agrupan en un lote, por lo que la estrategia de particionamiento afecta directamente la eficiencia del procesamiento por lotes.

Mensajes con clave

Para los mensajes con clave, el productor calcula el hash de la clave y selecciona una partición según el resultado. Los mensajes con la misma clave siempre se dirigen a la misma partición.

Mensajes sin clave

Antes de Kafka 2.4, la estrategia predeterminada para mensajes sin clave era round-robin: cada mensaje iba a la siguiente partición en secuencia. Esto dispersaba los mensajes en todas las particiones, generaba muchos lotes pequeños y aumentaba la latencia.

Kafka 2.4 introdujo la estrategia de particionamiento adhesivo (KIP-480) para resolver este problema. En lugar de rotar las particiones por mensaje, el productor se adhiere a una partición hasta que el lote actual está lleno y luego selecciona aleatoriamente una nueva partición. Con el tiempo, los mensajes siguen distribuyéndose uniformemente en todas las particiones, pero los lotes son mucho más grandes. Este enfoque evita el desequilibrio de particiones, reduce la latencia y mejora el rendimiento general del servicio.

Habilitación del particionamiento adhesivo

  • Cliente versión 2.4 o posterior: El particionamiento adhesivo es el valor predeterminado. No se requiere configuración.

  • Cliente anterior a la versión 2.4: Implemente un particionador personalizado y configúrelo mediante partitioner.class. A continuación, el ejemplo cambia de partición a un intervalo de tiempo configurable:

public class MyStickyPartitioner implements Partitioner {

    // Records the time of the last partition switch.
    private long lastPartitionChangeTimeMillis = 0L;
    // Records the current partition.
    private int currentPartition = -1;
    // The partition switch interval. Set the interval as needed.
    private long partitionChangeTimeGap = 100L;

    public void configure(Map<String, ?> configs) {}

    /**
     * Compute the partition for the given record.
     *
     * @param topic The topic name
     * @param key The key to partition on (or null if no key)
     * @param keyBytes serialized key to partition on (or null if no key)
     * @param value The value to partition on or null
     * @param valueBytes serialized value to partition on or null
     * @param cluster The current cluster metadata
     */
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {

        // Get all partition information.
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();

        if (keyBytes == null) {
            List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
            int availablePartitionSize = availablePartitions.size();

            // Check the current active partitions.
            if (availablePartitionSize > 0) {
                handlePartitionChange(availablePartitionSize);
                return availablePartitions.get(currentPartition).partition();
            } else {
                handlePartitionChange(numPartitions);
                return currentPartition;
            }
        } else {
            // For a message with a key, select a partition based on the key's hash value.
            return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
        }
    }

    private void handlePartitionChange(int partitionNum) {
        long currentTimeMillis = System.currentTimeMillis();

        // If the time since the last switch exceeds the switch interval, switch to the next partition. Otherwise, use the current partition.
        if (currentTimeMillis - lastPartitionChangeTimeMillis >= partitionChangeTimeGap
            || currentPartition < 0 || currentPartition >= partitionNum) {
            lastPartitionChangeTimeMillis = currentTimeMillis;
            currentPartition = Utils.toPositive(ThreadLocalRandom.current().nextInt()) % partitionNum;
        }
    }

    public void close() {}

}

Prevención de errores de memoria insuficiente (OOM)

El productor almacena mensajes en caché en la memoria antes de enviar los lotes. Si la caché crece demasiado, se produce un error de memoria insuficiente (OOM).

Parámetro

Descripción

Valor predeterminado

Recomendación

buffer.memory

Memoria total disponible para el búfer de envío del productor, en bytes. Si el búfer es demasiado pequeño, la asignación de memoria puede tardar mucho, afectar el rendimiento de envío y causar tiempos de espera agotados.

33554432 (32 MB)

Establezca como mínimo batch.size x número de particiones x 2. El valor predeterminado de 32 MB es suficiente para un solo productor.

Importante

Ejecutar varios productores en la misma máquina virtual Java (JVM) multiplica el uso de memoria. Cada productor asigna su propia buffer.memory: cuatro productores con el valor predeterminado de 32 MB consumen 128 MB de heap. En producción, generalmente basta con un solo productor por aplicación. Si debe ejecutar varios productores, reduzca buffer.memory para cada uno o aumente el tamaño del heap de la JVM según corresponda.

Orden de las particiones

Dentro de una sola partición, los mensajes se almacenan y consumen en el orden en que se envían.

De forma predeterminada, ApsaraMQ for Kafka no garantiza un orden estricto dentro de una partición. Durante una actualización o un failover, una pequeña cantidad de mensajes puede desordenarse al redirigirse a otra partición. Esta compensación mejora la disponibilidad.

Para aplicar un orden estricto dentro de una partición, seleccione local storage al crear el topic.