一、生产者如何提高吞吐量
下面参数可以提高kafka的吞吐量:
- batch.size:批次大小,默认16k
- linger.ms:等待时间,默认值为0,生产环境中修改为5-100ms
- compression.type:压缩一般使用 snappy
- RecordAccumulator:缓冲区大小,默认为32,生产环境可修改为64m
package com.kafka.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
/**
* @author wangbo
* @version 1.0
*/
/**
* 生产者如何提高吞吐量,提高效率
*/
public class CustomProducerParameters_06 {
public static void main(String[] args) {
//配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop100:9092,hadoop102:9092"); //写两个节点是为了防止客户挂掉,另一个能够正常工作
//指定对应的key和value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
/**
* 下面四个参数可以提高kafka的吞吐量
*/
//(1)缓冲区大小,默认为32M
properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG,33554432);
//(2)批次大小,默认为16k
properties.put(ProducerConfig.BATCH_SIZE_CONFIG,16384);
//(3)linger.ms,等待时间,默认值为0,生产环境中修改为5-100ms
properties.put(ProducerConfig.LINGER_MS_CONFIG,1);
//(4)压缩
properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"snappy");
// 1.创建kafka生成对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
// 2.发送数据
for (int i = 0; i<5;i++){
//第一个参数为生产者的主题名,第二个生产者生产的数据value, new Callback()创建回调函数
kafkaProducer.send(new ProducerRecord<String, String>("first3", "kafka" + i), new Callback() {
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if (e == null){
System.out.println("主题为:" + recordMetadata.topic() + " 分区为:" + recordMetadata.partition());
}
}
});
}
// 3.关闭资源
kafkaProducer.close();
}
}
二、数据可靠性
首先了解acks应答原理,acks应答级别有三种
- acks=0:生产者发送过来的数据,不需要等数据落盘应答
- acks=1:生产者发送过来的数据,Leader收到数据后应答
- acks=-1(all):生产者发送过来的数据,Leader+和isr队列里面的所有节点收齐数据后应答。-1和all等价
优缺点分析
- acks=0,生产者发送过来数据就不管了,可靠性差,效率高(可能丢失数据);
- acks=1,生产者发送过来数据Leader应答,可靠性中等,效率中等(可能丢失数据);
- acks=-1,生产者发送过来数据Leader和ISR队列里面所有Follwer应答,可靠性高,效率低;
如果想数据完全可靠:
- ACK级别设置为-1
- 主题分区数大于等于2
- 主题ISR(所有的Follower+Leader)里应答的最小副本数量大于等于2
总结:在生产环境中,acks=0很少使用;acks=1,一般用于传输普通日志,允许丢个别数据;acks=-1,一般用于传输和钱相关的数据,对可靠性要求比较高的场景。
package com.kafka.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
/**
* @author wangbo
* @version 1.0
*/
/**
* 生产者如何提高数据的可靠性
*/
public class CustomProducerAck_07 {
public static void main(String[] args) {
//配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop100:9092,hadoop102:9092"); //写两个节点是为了防止客户挂掉,另一个能够正常工作
//指定对应的key和value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
//acks,测试中看不出差别,数据量大的时候1和-1还是有区别的
properties.put(ProducerConfig.ACKS_CONFIG,"1");
//重试次数,默认是int的最大值,修改为3次
properties.put(ProducerConfig.RETRIES_CONFIG,3);
// 1.创建kafka生成对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
// 2.发送数据
for (int i = 0; i<5;i++){
//第一个参数为生产者的主题名,第二个生产者生产的数据value, new Callback()创建回调函数
kafkaProducer.send(new ProducerRecord<String, String>("first3", "kafka" + i), new Callback() {
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if (e == null){
System.out.println("主题为:" + recordMetadata.topic() + " 分区为:" + recordMetadata.partition());
}
}
});
}
// 3.关闭资源
kafkaProducer.close();
}
}
三、数据去重
问题一
(1)如果想数据完全可靠即下面三个条件,但是不能保证数据不重复
- ACK级别设置为-1
- 主题分区数大于等于2
- 主题ISR(所有的Follower+Leader)里应答的最小副本数量大于等于2
(2)ACK级别设置为0,不能保证数据不丢失
问题引入:对于一些非常重要的信息,比如和钱相关的数据,要求数据既不能重复也不丢失,该怎么办
Kafka 0.11版本以后,引入了一项重大特性:幂等性和事务
1.幂等性
幂等性就是指Producer(生产者)不论向Broker发送多少次重复数据,Broker端都只会持久化一条,保证了不重复
保证数据既不能重复也不丢失要求如下:
- 实现幂等性
- 数据完全可靠(ack=-1 + 分区副本数>=2 + ISR最小副本数量>=2)
kafka重复数据的判断标准:
如果kafka生产的消息 PID, Partition, SeqNumber 只要这三个属性相同时 Broker(服务器)就会认为是一条数据,只会持久化一条
PID(生产者的ID):Kafka每次重启都会分配一个新的ID
Partition:分区号
SeqNumber:是一个单调自增的数值
总结:幂等性只能保证的是在单分区单会话内不重复。所谓的单会话Kafka只启动一次,多会话Kafka启动多次,那么PID就会发生改变,就不能保证数据不重复
如何使用幂等性
开启参数 enable.idempotence 默认为 true,关闭false ,即默认就是打开的
2.生产者事务
引入:由于幂等性不能完全保证数据不重复,那么就可以用事物来解决
说明:开启事务,必须开启幂等性(默认开启)
Kafka 的事务一共有如下 5 个 API:
- 初始化事务:void initTransactions();
- 开启事务:void beginTransaction() throws ProducerFencedException;
- 在事务内提交已经消费的偏移量(主要用于消费者):void sendOffsetsToTransaction(Map<TopicPartition,OffsetAndMetadata> offsets,String consumerGroupId) throws ProducerFencedException;
- 提交事务:void commitTransaction() throws ProducerFencedException;
- 放弃事务(类似于回滚事务的操作):void abortTransaction() throws ProducerFencedException;
注意:Producer (生产者)在使用事务功能前,必须先自定义一个唯一的 transactional.id。有 了 transactional.id,即使客户端挂掉了,它重启后也能继续处理未完成的事务
package com.kafka.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
/**
* @author wangbo
* @version 1.0
*/
/**
* 生产者通过 事物 避免数据重复
*/
public class CustomProducerTransactions_08 {
public static void main(String[] args) {
//配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop100:9092,hadoop102:9092"); //写两个节点是为了防止客户挂掉,另一个能够正常工作
//指定对应的key和value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
//手动指定事物的ID,事物id就叫kafka123
properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG,"kafka123");
// 1.创建kafka生成对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
//事物初始化
kafkaProducer.initTransactions();
//开启事物
kafkaProducer.beginTransaction();
try {
// 2.发送数据
for (int i = 0; i<5;i++){
//第一个参数为生产者的主题名,第二个生产者生产的数据value, new Callback()创建回调函数
kafkaProducer.send(new ProducerRecord<String, String>("first3", "kafka" + i), new Callback() {
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if (e == null){
System.out.println("主题为:" + recordMetadata.topic() + " 分区为:" + recordMetadata.partition());
}
}
});
}
//提交事物
kafkaProducer.commitTransaction();
}catch (Exception e){
//终止事物
kafkaProducer.abortTransaction();
}finally {
// 3.关闭资源
kafkaProducer.close();
}
}
}
四、数据有序和乱序
kafka单分区内的数据是有序的;多分区 即分区与分区间是乱序的
(1)kafka在1.x版本之前保证数据单分区有序,条件如下:
max.in.flight.requests.per.connection=1(不需要考虑是否开启幂等性),因为数据是一条一条被消费的不会存在乱序
(2)kafka在1.x及以后版本保证数据单分区有序,条件如下:
- 未开启幂等性
max.in.flight.requests.per.connection需要设置为1 - 开启幂等性
max.in.flight.requests.per.connection需要设置小于等于5
原因说明:因为在kafka1.x以后,启用幂等后,kafka服务端会缓存producer发来的最近5个request的元数据,故无论如何,都可以保证最近5个request的数据都是有序的。