kafka消息
一、Kafka基本概念
1.Kafka作用
1.1 消息系统
消息中间件,具有异步、削锋、解耦和异步通信
1.2 储存系统
Kafka将信息持久到化磁盘中,Kafka消息持久化功能和多副本机制
1.3 流式处理平台
2.Kafka 基本信息
Productor将消息发送到broker中,broker将消息进行持久化到磁盘中,Consumer 负责从broker进行订阅消息
Productor: 生产者,负责发送消息,将其投递到Kafka中
Broker: 服务代理节点,可以将broker看做是一个kafka服务
Consumer: 消费者,负责在broker进行订阅消息
Topic: 主题,Kafka消息是按照主题进行分类,生产者将消息发送到特定的主题上。而消费者进行对指定的主题的消息进行消费
Partition: 同一种主题下的分区消息是不同的,通过offset进行确保消息的顺序性,是消息在分区中的唯一标识:合理使用存储资源:可以把海量的数据按照分区进行切割一块数据存储到多台Broker中,实现负载均衡的效果。提高并行度,消费者可以以分区进行消费。
二、生产者
1.必要参数设置
bootstrap.servers:指定生产者连接的Kafka的Broker的地址清单; 指定消息的具体的编码格式: key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer
2,消息的发送
2.1 发送消息的三种模式
1.发后即忘:发送消息之后,只管消息是否发送,不关心消息是否发送成功 2.同步:发送消息到RecoedAccumlator,等消息收集器中的消息在发送到Kafka集群中,在进行再次发送消息;
ProducerRecord<String, String> producerRecord = new ProducerRecord<>("zhTest",""+i, "msg" + i); try { kafkaTemplate.send(producerRecord).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
3.异步:发送消息到RecoedAccumlator,不用等消息收集器中的消息在发送到Kafka集群中;
# 有回调的发送
KafkaProducer<String,String> kafkaProducer=new KafkaProducer<String, String>(properties);
for (int i = 0; i < 5; i++) {
kafkaProducer.send(new ProducerRecord<>("first", "sq" + i), new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if(exception==null){
System.out.println("主题"+metadata.topic()+"分区"+metadata.partition());
}
}
});
}
# 无回调发送
KafkaProducer<String,String> kafkaProducer=new KafkaProducer<String, String>(properties);
for (int i = 0; i < 5; i++) {
kafkaProducer.send(new ProducerRecord<>("first","sq"+i));
}
2.2 消息序列化
生产者需要通过序列化器将对象转换为字节数组才能将消息发送到Kafka 消费者需要通过反序列换器将字节数组转化为对象
2.3 消息发送
消息通过send()发送到broker中,要经过拦截器/序列化器/分区器 DefaultPartitioner:默认的分区器,在默认分区器实现中,close() 方法是空方法,如果key不是null,那么默认的分区器就会对key进行哈希,最终得到的哈希值进行区分分区号。如果key和partition没有指定,那么就会通过轮询的方式进行发往主题内的各个分区。
# 自定义分区器
private final AtomicInteger counter=new AtomicInteger();
@Override
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
List<PartitionInfo> list=cluster.partitionsForTopic(topic);
int num=list.size();
if(keyBytes==null){
return counter.getAndIncrement()%num;
}else{
return Utils.toPositive(Utils.murmur2(keyBytes))%num;
}
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> configs) {
}
2.4 原理分析
主线程和sender 线程:
主线程:通过拦截器/序列化器/分区器将消息发送到消息累加器;消息累加器主要是方便消息进行批量发送,从而减少网络传输。
sender线程:将消息发送到Kafka中
2.5 重要参数设置
设置等待acks返回的机制,有三个值 0:不等待返回的acks(可能会丢数据,因为发送消息没有了失败重试机制,但是这是最低延迟):可靠性最差,效率高 1:消息发送给kafka分区中的leader后就返回(如果follower没有同步完成leader就宕机了,就会丢数据):一般传输用户的普通日志,允许丢个别数据; -1(默认):等待所有follower同步完消息后再发送(绝对不会丢数据):传输比较重要的数据 spring.kafka.producer.acks=-1
-1:生产者发送过来数据,Leader和ISR队列中的所有的节点收齐数据进行应答;
如果Follower长时间没有Leader发送应答信息,则该Follower则被踢出ISR队列,这样就不用等长时间联系不上的节点进行应答
数据完全可靠性条件=ACK级别设置为-1+分区副本大于2(至少有一个Follower)+ISR应答的最小副本两大于等于2
问题:数据重复?
幂等性:
PID:kafka每次重启都会分配一个新的;Partion:分区号,sequence:序列号,是递增的
事务:
数据有序: 默认保证同一个分区的数据是有序的,则可以设置topic只使用一个分区,这样消息就全局有序。但是降低了性能,不适合高并发情况;
productor发送消息指定需要保证顺序的几条消息送到同一个分区,这样消费者消费就是有序的。
同一张表的数据可以放到同一个分区。
数据乱序:
保证单分区消息有序
未开启幂等性:max.in.flight.requests.per.connection=1
开启幂等性:max.in.flight.requests.per.connection<=5
2.6 生产者如何提高吞吐量
.batch.size:批次大小,默认16k .linger.ms:等待时间,修改为5-100ms .compression.type:压缩为snappy RecordAccumulator:缓存区大小,修改为64M
二、消费者
2.1 消费方式:拉模式
为啥不用推模式:因为broker的发送消息效率,很难适应所有的消费者的消费速率。
2.2 消费者组
由多个consumer组成,形成一个消费组的条件,是所有消费者的groupid相同;
消费者组内的每个消费者负责消费不同分=分区的数据,一个分区只能有一个组内消费者消费;
2.3 消费者组的初始化
2.4 消费者组的详细消费流程
2.5 分区分配策略
2.5.1 Range
首先对同一个topic里面的分区按照序号进行排列,消费者也按照序号进行排列;
通过分区数/消费者数来决定每个消费者应该消费几个分区,如果除不尽,那么前面几个消费者会多消费一个分区;
容易产生数据倾斜
2.5.2 Roundrobin
轮询分配策略
2.5.3 Sticky
粘性分配策略
1.分区的分配尽可能要均匀
2.分区的分配尽可能与上次分配的保持相同
2.6 提交偏移量offset
(1) 自动提交偏移量:
Kafka中偏移量的自动提交是由参数enable_auto_commit和auto_commit_interval_ms控制的,当enable_auto_commit=True时,Kafka在消费的过程中会以频率为auto_commit_interval_ms向Kafka自带的topic(__consumer_offsets)进行偏移量提交,具体提交到哪个Partation是以算法:partation=hash(group_id)%50来计算的。
如:group_id=test_group_1,则partation=hash("test_group_1")%50=28
(2)手动提交偏移量
1.同步手动提交偏移量
同步模式下提交失败的时候一直尝试提交,直到遇到无法重试的情况下才会结束,同时同步方式下消费者线程在拉取消息会被阻塞,在broker对提交的请求做出响应之前,会一直阻塞直到偏移量提交操作成功或者在提交过程中发生异常,限制了消息的吞吐量。
2.同步手动提交偏移量
异步手动提交offset时,消费者线程不会阻塞,提交失败的时候也不会进行重试,并且可以配合回调函数在broker做出响应的时候记录错误信息。
三、Broker
3.1 broker的工作流程
四、Kafka原理
4.1 kafka写流程
1.broker进程上的leader将消息写入到本地log中
2.follower将leader上的信息拉取下来,写入到本地log中,并向leader发送Ack
3.leader接收到所有的ISR中的回复的Ack后,并向生产者返回Ack。
4.2 Kafka消费流程
1.每个Consumer都可以根据分配策略获得消费分区,
2.获取到consumer的对应offset
3.找到该分区的leader,拉取数据
4.消费者提交offset
4.3 Kafka 的数据存储形式
4.3.1 kafka 数据存储组成
1.一个topic由多个分区组成
2.一个分区由多个segment
3.一个segment是由多个文件组成(index,log,timeindex)
4.3.2 kafka数据积压
因为网络延迟导致消费失败:
重新配置消费者的超时间,将消费的超时时间改为更大点;
4.3.3 Kafka的数据清理
Kafka的消息存储在磁盘中,为了控制磁盘的占用空间,kafka需要不断的对过去的信息进行清理。
1.日志删除:按照指定策略直接删除不符合条件的日志
2.日志压缩:按照指定的key进行整合,有相同的key但是不同value值,只保存最后一个版本。
log.cleaner.enable:true 开启自动清理日志功能
log.clearnup.policy:delete: 删除日志
log.cleanup.policy:compact:压缩日志
日志删除策略:
基于时间、基于日志大小、基于日志起始偏移量的保留策略
\