生产者
创建生产者
// 创建以下三种必须的配置
private Properties kafkaProps = new Properties();
// 创建broker的地址清单,格式host:port,建议至少给两个,如果出现宕机,生产者可以继续连接
kafkaProps.put("bootstrap.servers","broker1:9092,broker2:9092");
// 指定序列化器
kafkaProps.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
kafkaProps.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<>(kafkaProps);
发送消息
发送消息的三种方式
-
发送并忘记【fire-and-forget】
只发送,不关心是否正常到达,有时会丢失消息
-
同步发送
使用
send()
方法发送消息,会返回Future
对象,调get()
方法等待,就可以知道消息是否会发送成功 -
异步发送
使用
send()
方法发送并指定一个回调函数,都武器在返回响应时调用该函数
// 发送并忘记
ProducerRecord<Object, String> record = new ProducerRecord<>("CustomerCountry","Pecision Products","France");
try{
producer.send(record);
}catch (Exception e){
e.printStackTrace();
}
// 同步发送消息
ProducerRecord<Object, String> record = new ProducerRecord<>("CustomerCountry","Pecision Products","France");
try{
producer.send(record).get();
}catch (Exception e){
e.printStackTrace();
}
// 异步发送
private class DemoProducerCallback implements Callback{
@Override
public void onCompletion(RecordMetadata recordMetadata,Exception e){
if (null != e){
e.printStackTrace();
}
}
}
ProducerRecord<Object, String> record = new ProducerRecord<>("CustomerCountry","Biomedical Materials","USA");
producer.send(record,new DemoProducerCallback());
生产者配置
-
acks
必须有多少的分区副本接受消息,才会认为消息写入为成功
- acks=0 :生产者在成功写入消息前不会等待服务器的响应,如中间出现问题会导致消息丢失,但因无需等待,可达到较高的吞吐量
- acks=1 :只要集群的首领节点收到消息,生产者就会收到一个来自服务器的成功响应。如果没有消息到达首领节点(首领节点死亡,正在选举),生产者会接受错误响应,并重发。如果没有收到消息的节点选举为首领,则消息丢失。吞吐量会取决于同步,异步发送
- acks=all :当所有参与复制的节点收到全部消息时,生产者才会收到服务器的成功响应,这种模式是最安全的,但延迟也更高。
-
buffer.memory
设置生产者内存缓冲区的大小,生产者用它缓冲要发送到服务器的消息,若应用程序发送消息的速度大于发送到服务器的速度,则会导致
send()
方法阻塞,或者异常【取决于block.on.buffer.full
(0.9.00版本为max.block.ms
)配置,其表示在抛出异常时阻塞的时间】 -
compression.type
默认不压缩。其值可设置为:snappy【占用较少cpu,提供较好的性能和压缩比】、gzip【占用较高的cpu,更高的压缩比】、lz4。指定的是消息发送前的算法
-
retries
消费者重发消息的次数,重试之间会等待100ms【可通过
retry.backoff.ms
修改】 -
bacth.size
设置一个批次可使用内存的大小【字节数】,发送时并不是等填满才发送,所以设置过大不会造成消息延迟,只会占用大量内存,设置过小,会频繁发送,增加额外开销。
-
linger.ms
指定生产者在发送批次之前等待加入批次的时间。【默认只要有可用线程生产者就会把消息发送出去】
-
client.id
任意字符串即可,服务器用来识别消息来源,也可以用在日志和配额的指标当中
-
max.in.flight.requests.per.connection
生产者在收到服务器响应之前可发送的消息,值越高,占用内存越高,吞吐量越高
-
timeout.ms
request.timeout.ms
metadata.fetch.timeout.ms
timeout.ms
生产者发送数据时等待服务器响应的时间request.timeout.ms
生产者获取元数据时等待服务器返回响应的时间metadata.fetch.timeout.ms
broker等待同步副本返回消息确认的时间 与acks
相匹配 -
max.block.ms
指定调用
send()
方法或partitionsFor()
方法获取元数据时生产者阻塞的时间,到达配置时间即抛出异常 -
max.request.size
控制生产者发送请求的大小
-
receive.buffer.bytes
send.buffer.bytes
指定TCP socket接受和发送数据包的缓冲区大小。值为-1,则为操作系统默认配置。
序列化器
可使用序列化框架Avro
Thrift
Protobuf
等,或自定义序列化器
分区
创建ProducerRecord
对象时,可指定key,也可不指定。key决定消息被写到主题哪个分区,也可做为附件信息
// 不设置key的创建方式,此时key为null
ProducerRecord<Object, String> record = new ProducerRecord<>("CustomerCountry","USA");
-
key 为null 且使用默认的分区器,分区器会使用轮询算法将消息均衡的分布到各个分区上。
-
key 不为null 且使用了默认的分区器,Kafka会使用自己的散列算法对key进行散列,进行散列会使用所有分区。
-
只有在不改变主题分区数量的情况下,键与分区之间的映射才能保持不变,所以不要增加新分区
自定义分区器
public class BananaPartitioner implements Partitioner {
@Override
public int partition(String s, Object o, byte[] bytes, Object o1, byte[] bytes1, Cluster cluster) {
return 0;
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> map) {
}
}