Tabla de contenido
1. Envío de mensajes
1. Análisis del proceso de producción de datos del cliente de Kafka Java
- Para construir un primer
ProducerRecord
objeto que se pueda declarar tema Tema , partición Partición , clave Clave y valor Valor , temas y valores que se deben declarar, no se pueden especificar particiones y claves. - Llame al método send () para enviar el mensaje.
- Debido a que el mensaje se va a transmitir en la red, debe ser serializado La función del serializador es serializar la clave y los objetos de valor del mensaje en una matriz de bytes.
- Los siguientes datos de la partición son, si entre los
ProducerRecord
objetos especificados, la partición no hará nada directamente al retorno de la partición especificada; si no, entonces la partición se seleccionará en función de una clave de partición, una buena elección Después de la partición, el productor sabe a qué tema y partición enviar los registros. - Luego, este registro se agregará a un lote de registros y todos los mensajes de este lote se enviarán al mismo tema y partición. Habrá un hilo separado para enviar estos lotes de registros al Broker correspondiente.
- El corredor recibe correctamente el mensaje, lo que indica que la transmisión se ha realizado correctamente y devuelve los metadatos del mensaje (incluida la información sobre el tema y la partición y el desplazamiento registrado en la partición) . El envío falló, puede optar por volver a intentarlo o lanzar una excepción directamente.
Paquetes dependientes <kafka.version>2.0.0</kafka.version>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_${
scala.version}</artifactId>
<version>${
kafka.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
2. Configuración de parámetros necesaria
Ver el código base:com.heima.kafka.chapter2.KafkaProducerAnalysis
public static Properties initConfig() {
Properties props = new Properties();
// 该属性指定 brokers 的地址清单,格式为 host:port。清单里不需要包含所有的 broker 地址,
// 生产者会从给定的 broker 里查找到其它 broker 的信息。——建议至少提供两个 broker 的信息,因为一旦其中一个宕机,生产者仍然能够连接到集群上。
props.put("bootstrap.servers", brokerList);
// 将 key 转换为字节数组的配置,必须设定为一个实现了 org.apache.kafka.common.serialization.Serializer 接口的类,
// 生产者会用这个类把键对象序列化为字节数组。
// ——kafka 默认提供了 StringSerializer和 IntegerSerializer、 ByteArraySerializer。当然也可以自定义序列化器。
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
// 和 key.serializer 一样,用于 value 的序列化
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
// 用来设定KafkaProducer对应的客户端ID,默认为空,如果不设置KafkaProducer会自动 生成一个非空字符串。
// 内容形式如:"producer-1"
props.put("client.id", "producer.client.id.demo");
return props;
}
Properties props = initConfig();
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// KafkaProducer<String, String> producer = new KafkaProducer<>(props,
// new StringSerializer(), new StringSerializer());
//生成 ProducerRecord 对象,并制定 Topic,key 以及 value
ProducerRecord<String, String> record = new ProducerRecord<>(topic, "hello, Kafka!");
try {
// 发送消息
producer.send(record);
}
3. Tipo de entrega
Enviar y olvidar
producer.send(record)
Enviar sincrónicamente
//通过send()发送完消息后返回一个Future对象,然后调用Future对象的get()方法等待kafka响应
//如果kafka正常响应,返回一个RecordMetadata对象,该对象存储消息的偏移量
// 如果kafka发生错误,无法正常响应,就会抛出异常,我们便可以进行异常处理
producer.send(record).get();
Envío asincrónico
producer.send(record, new Callback() {
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception == null) {
System.out.println(metadata.partition() + ":" + metadata.offset());
}
}
});
4. Serializador
Los mensajes deben serializarse para ser transmitidos en la red, y la función del serializador es solo eso.
Kafka proporciona el serializador de cadenas predeterminado ( org.apache.kafka.common.serialization.StringSerializer
), así como los serializadores integer ( IntegerSerializer
) y byte array ( BytesSerializer
). Todos estos serializadores implementan la interfaz ( org.apache.kafka.common.serialization.Serializer
) y básicamente pueden satisfacer las necesidades de la mayoría de los escenarios.
5. Serializador personalizado
Ver el código base:com.heima.kafka.chapter2.CompanySerializer
/**
* 自定义序列化器
*/
public class CompanySerializer implements Serializer<Company> {
@Override
public void configure(Map configs, boolean isKey) {
}
@Override
public byte[] serialize(String topic, Company data) {
if (data == null) {
return null;
}
byte[] name, address;
try {
if (data.getName() != null) {
name = data.getName().getBytes("UTF-8");
} else {
name = new byte[0];
}
if (data.getAddress() != null) {
address = data.getAddress().getBytes("UTF-8");
} else {
address = new byte[0];
}
ByteBuffer buffer = ByteBuffer. allocate(4 + 4 + name.length + address.length);
buffer.putInt(name.length);
buffer.put(name);
buffer.putInt(address.length);
buffer.put(address);
return buffer.array();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new byte[0];
}
@Override
public void close() {
}
}
- Utilice un serializador personalizado
Ver el código base:com.heima.kafka.chapter2.ProducerDefineSerializer
public class ProducerDefineSerializer {
public static final String brokerList = "localhost:9092";
public static final String topic = "heima";
public static void main(String[] args) throws ExecutionException, InterruptedException {
Properties properties = new Properties();
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, CompanySerializer.class.getName());
// properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
// ProtostuffSerializer.class.getName());
properties.put("bootstrap.servers", brokerList);
KafkaProducer<String, Company> producer = new KafkaProducer<>(properties);
Company company = Company.builder().name("kafka") .address("北京").build();
// Company company = Company.builder().name("hiddenkafka")
// .address("China").telphone("13000000000").build();
ProducerRecord<String, Company> record = new ProducerRecord<>(topic, company);
producer.send(record).get();
}
}
6. Particionador
El propio Kafka tiene su propia estrategia de partición. Si no se especifica, se utilizará la estrategia de partición predeterminada:
Kafka asigna particiones de acuerdo con la clave del mensaje, es decir hash(key) % numPartitions
. Si la clave es la misma, se asignará a una partición unificada.
org.apache.kafka.clients.producer.internals.DefaultPartitioner
Análisis de código fuente
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
int numPartitions = partitions.size();
if (keyBytes == null) {
int nextValue = this.nextValue(topic);
List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
if (availablePartitions.size() > 0) {
int part = Utils.toPositive(nextValue) % availablePartitions.size();
return ((PartitionInfo)availablePartitions.get(part)).partition();
} else {
return Utils.toPositive(nextValue) % numPartitions;
}
} else {
return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
}
}
- Particionador personalizado ver código base
com.heima.kafka.chapter2.DefinePartitioner
/**
* 自定义分区器
*/
public class DefinePartitioner implements Partitioner {
private final AtomicInteger counter = new AtomicInteger(0);
@Override
public int partition(String topic, Object key, byte[] keyBytes,Object value, byte[] valueBytes, Cluster cluster) {
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
int numPartitions = partitions.size();
if (null == keyBytes) {
return counter.getAndIncrement() % numPartitions;
} else return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> configs) {
}
}
- Para implementar un particionador personalizado debe
ProducerConfig.PARTITIONER_CLASS_CONFIG
implementarse a través de parámetros de configuración
// 自定义分区器的使用
props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,DefinePartitioner.class.getNam e());
7. Interceptor
Producer Interceptor ( interceptor
) es una característica bastante nueva , que junto con el interceptor del lado del consumidor se introdujeron en la versión 0.10 de Kafka y se utilizan principalmente para implementar una lógica de control personalizada en el lado del cliente .
El interceptor productor se puede utilizar para realizar un trabajo preparatorio antes de enviar el mensaje.
escenas que se utilizarán
- Filtra los mensajes no calificados de acuerdo con una regla determinada
- Modificar el contenido del mensaje
- Requisitos estadísticos
Ver código base: Interceptor personalizadocom.heima.kafka.chapter2.ProducerInterceptorPrefix
/**
* 自定义拦截器
*/
public class ProducerInterceptorPrefix implements ProducerInterceptor<String, String> {
private volatile long sendSuccess = 0;
private volatile long sendFailure = 0;
@Override
public ProducerRecord<String, String> onSend( ProducerRecord<String, String> record) {
String modifiedValue = "prefix1-" + record.value();
return new ProducerRecord<>(record.topic(), record.partition(), record.timestamp(), record.key(), modifiedValue, record.headers());
// if (record.value().length() < 5) {
// throw new RuntimeException();
// }
// return record;
}
@Override
public void onAcknowledgement( RecordMetadata recordMetadata, Exception e) {
if (e == null) {
sendSuccess++;
} else {
sendFailure++;
}
}
@Override
public void close() {
double successRatio = (double) sendSuccess / (sendFailure + sendSuccess);
System.out.println("[INFO] 发送成功率=" + String.format("%f", successRatio * 100) + "%");
}
@Override
public void configure(Map<String, ?> map) {
}
}
- Después de implementar un interceptor personalizado, debe especificar el interceptor en los parámetros de configuración. El valor predeterminado de este parámetro está vacío, de la siguiente manera:
// 自定义拦截器使用
props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,ProducerDefineSerializer.cla ss.getName());
- Manifestación:
Receptor remitente
Los estudiantes que necesiten el artículo de referencia "Manual técnico de Kafka" pueden agregar el asistente VX: C18173184271 Observaciones:CSDN Java_Caiyo ¡Obtenga información gratuita de Java!