Conocimiento profundo de la serie Kafka (2) - Productor Kafka

Directorio de artículos de serie

Artículos de la serie de guías autorizadas de Kakfa

Prefacio

Esta serie es mi transcripción y pensamientos después de leer el libro "La guía definitiva de Kafka".

texto

Productor de Kafka

Los principales pasos del envío de mensajes de Kafka

Primero ponga la imagen: los pasos principales para enviar un mensaje a Kafka se
Inserte la descripción de la imagen aquí
describen en palabras:

  1. Cree un objeto ProducerRecord que contenga atributos como el asunto y el contenido enviado.
  2. La clave y la partición especificadas se utilizan para enviar objetos ProducerRecord a la partición especificada. Y al enviar objetos, el productor necesita serializar las claves y los valores en matrices de bytes (necesitamos configurar el serializador) para que puedan transmitirse en la red.
  3. Los datos se envían al particionador, que determina a qué tema y partición se envía el mensaje. Luego, este registro se agrega a un lote de registros (RecordAccumulator, acumulador de mensajes).
  4. Una vez que el mensaje alcanza un cierto nivel, un hilo separado (remitente) enviará todos los mensajes del mismo lote al corredor correspondiente.
  5. Una vez que el servidor recibe el mensaje, devuelve una respuesta.

1. Si tiene éxito:
devuelva un objeto RecordMetaData, incluido el asunto, la partición y el desplazamiento.
2. Si falla:
devuelve un error. Después de recibir el error, el productor intentará reenviar el mensaje. Si falla después de un cierto número de veces, devolverá un mensaje de error.


Crear productor de Kafka (API)

Recuerde abrir Kafka con anticipación. Los pasos de instalación se encuentran en el blog anterior:
Comprensión profunda de la serie Kafka (1) -Primero conozca Kafka

El paquete de pom depende de:

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>0.11.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka_2.12</artifactId>
    <version>0.11.0.0</version>
</dependency>

Demostración simple:

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;

import java.util.Properties;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Properties properties = new Properties();
        properties.put("bootstrap.servers", "192.168.237.130:9092");
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
        for (int i = 0; i < 3; i++) {
    
    
            producer.send(new ProducerRecord<String, String>("test", Integer.toString(i), "message" + i));
        }
        producer.close();
    }
}

Después de la ejecución, encontrará en el terminal:
Inserte la descripción de la imagen aquí
Verificación:
Copie una sesión, ingrese el comando:

./bin/kafka-console-consumer.sh --zookeeper 192.168.237.130:2181 --from-beginning --topic test

Se puede ver que el mensaje se escribió correctamente.
Inserte la descripción de la imagen aquí


La siguiente es una introducción detallada a la API del productor:

Explicación detallada de los parámetros de productor de Kafka

La demostración anterior es el productor de Kafka más básico. Como puede observar, solo configuro 3 atributos, y estos 3 atributos deben configurarse.

  1. bootstrap.servers

Este atributo especifica la lista de direcciones del agente y el formato de la dirección es host: puerto.
Si Kafka es un clúster, no es necesario escribir todos los corredores, porque el productor encontrará la información de otros corredores de un corredor dado, pero tenga en cuenta: se recomienda proporcionar al menos 2 nodos de corredor para evitar otros problemas causados ​​por falta del tiempo.

  1. key.serializer

Los pares clave-valor del mensaje que el intermediario espera recibir son todos arreglos de bytes, y key.serializer
debe establecerse en una clase que implemente la interfaz org.apache.kafka.common.serialization.StringSerializer, y el productor usará esta clase El objeto clave se serializa en una matriz de bytes para la transmisión de red.

  1. value.serializer

Como key.serializer, debe configurarse.

  1. acks

El parámetro acks especifica cuántas réplicas de partición deben recibir el mensaje antes de que el productor considere que el mensaje se ha escrito correctamente. (Este parámetro es muy importante y tiene un gran impacto en la posibilidad de pérdida de mensajes)
(1) acks = 0:
1. El productor no esperará ninguna respuesta del servidor antes de escribir correctamente el mensaje.
2. Es decir: si hay un problema al enviar el mensaje, el productor no lo sabrá y el mensaje se perderá.
3. También debido a la segunda razón, el productor no necesita esperar la respuesta del servidor, por lo que este modo puede soportar la máxima velocidad para enviar mensajes, alto rendimiento.
(2) acks = 1
1. Siempre que el nodo maestro del clúster reciba un mensaje, el productor recibirá una respuesta.
2. Si el mensaje no se envía al nodo maestro o el nodo maestro está inactivo, un nodo esclavo que no ha recibido el mensaje se convierte en el nodo maestro. Ocurrirá la pérdida de mensajes.
(3) acks = all
1. El productor recibirá una respuesta exitosa del servidor cuando todos los nodos que participan en la replicación hayan recibido el mensaje. Este modo es el más seguro, pero tiene el rendimiento más bajo y la latencia más alta.

  1. buffer.memory

1. Se utiliza para establecer el tamaño del búfer de memoria del productor.
2. Debido a que el mensaje de Kafka se coloca primero en el búfer del productor, si el búfer está lleno, se inicia un hilo de remitente independiente para enviar el mensaje del búfer al servidor.

  1. tipo de compresión

1. De forma predeterminada, los mensajes no se comprimirán cuando se envíen. Este parámetro sirve para establecer el formato de compresión de los mensajes de Kafka.
2. Soporte de formato de compresión: rápido, gzip, lz4.

  1. reintentos

El valor de este parámetro determina la cantidad de veces que el productor puede repetir el mensaje, si se alcanza este número, el productor dejará de intentarlo y devolverá un error.

  1. tamaño del lote

1. Cuando sea necesario enviar varios mensajes a una partición, el productor los colocará en el mismo lote.
2. Y este parámetro determina el tamaño de la memoria que puede utilizar un lote, calculado según el número de bytes.

  1. persistir.ms

1. Este parámetro especifica el tiempo que el productor espera a que se agreguen más mensajes al lote antes de enviarlo.
2. kafkaProducer enviará el lote cuando el lote esté lleno o el parámetro alcance el límite superior.

  1. Identificación del cliente

Puede probar cualquier cadena para este parámetro y el servidor la usará para identificar la fuente del mensaje.

  1. max.in.flight.requests.por.conexión

1. Este parámetro determina cuántos mensajes puede enviar el productor antes de recibir la respuesta del servidor.
2. Cuanto mayor sea el valor, mayor será la memoria ocupada, pero mayor será el rendimiento.

  1. max.block.ms

1. Este parámetro especifica el tiempo de bloqueo del productor cuando se obtienen los metadatos cuando se llama al método send ().
2. ¿Cuándo ocurrió el bloqueo? Cuando el búfer de envío del productor está lleno o no hay metadatos disponibles, el método de envío se bloqueará.
3. Si se excede el tiempo de bloqueo, se lanza una excepción.

  1. tamaño máx. de solicitud

Este parámetro se utiliza para controlar el tamaño de la solicitud del mensaje enviado por el productor.

  1. recibir.buffer.bytes 和 enviar.buffer.bytes

Estos dos parámetros especifican respectivamente el tamaño del búfer del socket TCP que recibe y envía paquetes de datos.


Explicación detallada del método de envío del productor de Kafka

Hay tres formas principales de enviar mensajes en Kafka

  1. Envía y olvida (dispara y olvida)
  2. Enviar sincrónicamente
  3. Envío asincrónico

1. El método de envío más básico:

KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
// 构造格式: topic,key,value
ProducerRecord<String, String> record = new ProducerRecord<>("test", Integer.toString(4), "message" + 4);
try {
    
    
    producer.send(record);
} catch (Exception e) {
    
    
    e.printStackTrace();
} finally {
    
    
    producer.close();
}

2. Envíe mensajes de forma sincrónica:

KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
ProducerRecord<String, String> record = new ProducerRecord<>("test", Integer.toString(4), "message" + 4);
try {
    
    
	// 我们调用send方法返回的是一个Future对象。然后调用get方法等待kafka响应
	// 在调用Future的get()方法,若写入成功,则返回一个正确的相应RecordMetadata。
    RecordMetadata recordMetadata = producer.send(record).get();
    System.out.println(recordMetadata.offset());
} catch (Exception e) {
    
    
    e.printStackTrace();
} finally {
    
    
    producer.close();
}

resultado:
Inserte la descripción de la imagen aquí

3. Envíe mensajes de forma asincrónica:
personalice una clase de devolución de llamada (observe el paquete de la clase de implementación):

import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.RecordMetadata;

public class DemoProducerCallBack implements Callback {
    
    
    @Override
    public void onCompletion(RecordMetadata recordMetadata, Exception e) {
    
    
    	// 意思是不报错的话,我们会返回消息。
        if (e == null) {
    
    
            System.out.println("发送成功!");
        }
    }
}
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
ProducerRecord<String, String> record = new ProducerRecord<>("test", Integer.toString(5), "message" + 5);
try {
    
    
	// 直接将我们自定义的回调类放入send方法中即可
    producer.send(record, new DemoProducerCallBack());
} catch (Exception e) {
    
    
    e.printStackTrace();
} finally {
    
    
    producer.close();
}

resultado:
Inserte la descripción de la imagen aquí


Serializador

En primer lugar, debemos entender una cosa, ¿para qué se usa el serializador?
Para decirlo sin rodeos, es para que el mensaje generado por el productor se transmita sin problemas en la red.
Entonces, si queremos enviar un objeto como mensaje, sin duda, debemos personalizar un serializador, de lo contrario se reportará un error.

Demostración de serializador personalizado

Supongamos que queremos transmitir el objeto Cliente como mensaje. Clase de
cliente :

public class Customer {
    
    
    private int customerId;
    private String customerName;

    public Customer(int customerId, String customerName) {
    
    
        this.customerId = customerId;
        this.customerName = customerName;
    }

    public int getCustomerId() {
    
    
        return customerId;
    }

    public void setCustomerId(int customerId) {
    
    
        this.customerId = customerId;
    }

    public String getCustomerName() {
    
    
        return customerName;
    }

    public void setCustomerName(String customerName) {
    
    
        this.customerName = customerName;
    }
}

Serializador personalizado (tenga cuidado de no confundir el paquete):
CustomerSerializer

import org.apache.kafka.common.errors.SerializationException;
import org.apache.kafka.common.serialization.Serializer;

import java.nio.ByteBuffer;
import java.util.Map;

public class CustomerSerializer implements Serializer<Customer> {
    
    
    @Override
    public void configure(Map<String, ?> map, boolean b) {
    
    
        // 不做任何事
    }

    @Override
    public byte[] serialize(String topic, Customer data) {
    
    
        try {
    
    
            byte[] serializedName;
            int stringSize;
            if (data == null) {
    
    
                return null;
            } else {
    
    
                if (data.getCustomerName() != null) {
    
    
                    serializedName = data.getCustomerName().getBytes("UTF-8");
                    stringSize = serializedName.length;
                } else {
    
    
                    serializedName = new byte[0];
                    stringSize = 0;
                }
            }
            ByteBuffer buffer = ByteBuffer.allocate(4 + 4 + stringSize);
            buffer.putInt(data.getCustomerId());
            buffer.putInt(stringSize);
            buffer.put(serializedName);
            return buffer.array();
        } catch (Exception e) {
    
    
            throw new SerializationException("Error!!!!");
        }
    }

    @Override
    public void close() {
    
    

    }
}

Clase de prueba: (tenga en cuenta que el serializador ha cambiado)

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;

public class Test2 {
    
    
    public static void main(String[] args) {
    
    
        Properties properties = new Properties();
        // 这里的value,填你的自定义序列化器的引用地址。
        properties.put("bootstrap.servers", "192.168.237.130:9092");
        properties.put("key.serializer", "kafka.CustomerSerializer");
        properties.put("value.serializer", "kafka.CustomerSerializer");
        KafkaProducer<String, Customer> producer = new KafkaProducer<>(properties);
        Customer customer = new Customer(1, "hello");
        ProducerRecord record = new ProducerRecord("test", customer);
        try {
    
    
            producer.send(record);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            producer.close();
        }
    }
}

Resultado:
Inserte la descripción de la imagen aquí
para comparar, cree una clase de Cliente llamada Cliente2 (solo copie)
Si el código se cambia a:
Inserte la descripción de la imagen aquí
¿Ve cuál es el resultado?
Inserte la descripción de la imagen aquí
¿por qué? Porque nuestro serializador personalizado dice aquí:
Inserte la descripción de la imagen aquí
¡Correspondencia uno a uno!

Desventajas de usar un serializador personalizado:

  1. La demostración anterior es una entrada simple, entonces la pregunta es, si hay varios tipos de objetos en un proyecto real, si hay 100 objetos para ser transmitidos como mensajes, entonces ¿tenemos que crear 100 personalizados? ¿Qué pasa con el serializador? ?
  2. Por lo tanto, no se recomienda usar un serializador personalizado, es mejor usar algún marco de serializador, como: JSON, Avro, Protobuf, etc.

para resumir

Este artículo describe varios aspectos:
1. El proceso general del productor Kafka.
2. Los parámetros relevantes de la API del productor de Kafka y la forma de enviar mensajes.
3. Serializador Kafka.
El siguiente artículo ofrecerá una introducción detallada basada en los niveles de API y de consumidor de Kafka.

Supongo que te gusta

Origin blog.csdn.net/Zong_0915/article/details/109357082
Recomendado
Clasificación