进击的Kafka(三):生产者-原理

202 阅读1分钟

batch.size:只有数据累计到batch.size后,sender才会发送数据。默认16k linger.ms:如果迟迟没有达到batch.size,sender等待linger.ms设置时间之后,发送数据。单位:ms,默认0(没有延迟) acks设置:

  • 0:不需要等待数据落盘应答;
  • 1:leader落盘后应答;
  • -1(all):leader和follower落盘后应答;

一、ApI调用

(1)、异步API:

import org.apache.kafka.clients.producer.*;
import java.util.Properties;

public class KafkaProducerTest {

    public static void main(String[] args){
        //配置
        Properties properties = new Properties();
        //连接,如果是集群,不需要全部都配置,只需要配置几个
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.88:9092");
        //设置序列化方式
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
        //创建连接对象
        KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
        //发送数据--向first发送10条数据
        for (int i = 1; i <= 10; i++){
            kafkaProducer.send(new ProducerRecord("first","test1", "hello-kafka" + i));
        }
        //关闭资源
        kafkaProducer.close();
    }
}

image.png

(2)、带回调的异步API:

import org.apache.kafka.clients.producer.*;
import java.util.Properties;

public class KafkaProducerTest {

	public static void main(String[] args){
		//配置
		Properties properties = new Properties();
		//连接,如果是集群,不需要全部都配置,只需要配置几个
		properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.88:9092");
		//设置序列化方式
		properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
		properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
		//创建连接对象
		KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
		//发送数据--向first发送10条数据
		for (int i = 1; i <= 10; i++){
			kafkaProducer.send(new ProducerRecord("first", "test1", "hello-kafka" + i), new Callback() {
				@Override
				public void onCompletion(RecordMetadata recordMetadata, Exception e) {
					if(e == null){
						System.out.println("topic:"+recordMetadata.topic()+";partition:"+ recordMetadata.partition());
					}
				}
			});
		}
		//关闭资源
		kafkaProducer.close();
	}
}

控制台输出: image.png image.png

(3)、同步API:

同步API只是比异步API多加了一个 .get()

import org.apache.kafka.clients.producer.*;
import java.util.Properties;
import java.util.concurrent.ExecutionException;

public class KafkaProducerTest {

	public static void main(String[] args) throws ExecutionException, InterruptedException {
		//配置
		Properties properties = new Properties();
		//连接,如果是集群,不需要全部都配置,只需要配置几个
		properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.88:9092");
		//设置序列化方式
		properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
		properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
		//创建连接对象
		KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
		//发送数据--向first发送10条数据
		for (int i = 1; i <= 10; i++){
			kafkaProducer.send(new ProducerRecord("first", "test1", "同步API-发送数据" + i)).get();
		}
		//关闭资源
		kafkaProducer.close();
	}
}

image.png

二、生产者分区

(1)、好处:

  • 1.便于合理使用存储资源,每个Partition在一个Broker上存储,可以把海量的数据按照分区切割成一块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡的效果。
  • 2.提高并行度,生产者可以以分区为单位发送数据,消费者可以以分区为单位消费数据

(2)、默认分区策略

  • 1.如果指定分区,发往指定分区
  • 2.未指定分区,有key,用key的hash值对topic的partition数量取余,得到partition
  • 3.没有指定分区,也没有key,使用粘性分区(一批数据,一次一个分区)。尽可能一直用一个分区,该分区的batch.size/linger.ms已到,再随机选择一个分区,且和上一次的分区不同
//指定分区,发往指定分区
ProducerRecord(String topic, Integer partition, K key, V value);
//未指定分区,有key
ProducerRecord(String topic, K key, V value);
//未指定分区,也没有key
ProducerRecord(String topic, V value);

(3)、自定义分区策略

import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.PartitionInfo;

import java.util.List;
import java.util.Map;

public class MyPartitioner implements Partitioner {
	/**
	 * @param topic
	 * @param key 发送的key
	 * @param keyBytes
	 * @param value 发送的value
	 * @param valueBytes
	 * @param cluster
	 * @return
	 */
	@Override
	public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
		List<PartitionInfo> partitionInfos = cluster.availablePartitionsForTopic(topic);
		//分区数量
		int num = partitionInfos.size();
		//根据value与分区数求余的方式得到分区ID
		return value.hashCode() % num;
	}

	@Override
	public void close() {

	}

	@Override
	public void configure(Map<String, ?> map) {

	}
}

//在properties中设置自定义的分区策略 properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, MyPartitioner.class);