kafka 基础知识 第三讲

1.数据可靠性和一致性

数据可靠性-不丢失
一致性-读写一致性
可靠性:
1.topic 分区副本:kafka的分区多副本是kafka的可靠性保证的核心,把消息写入到多个副本kafka某个broker挂了也保证可靠性
2.producer 往broker发送消息:kafka在produce里面提供了消息确认机制(ack)ack=0,ack=1,ack=-1。
在这里插入图片描述
如果原来leader挂了,旧的8,9的可能被丢失了。isr。按顺序找leader。osr就从新的leader同步数据。当原来的leader挂了之后,会保留hw,水位线,然后重启把水位线后面的截取掉。再同步。
真正的可靠性是:ack=all/-1,然后配合min.insync.replicas 参数结合在一起。
3.isr同步副本列表以及leader选举机制
ISR(同步副本)每个分区的leader会维护一个isr列表,isr列表里面就是follower副本的broker编号,只有跟的leader的follower副本才能加入isr里面
只有ISR的成员才能选举成leader.
4.高水位线
hw(已经提交的最小值),当leader挂了之后,在其重新启动后,要清楚之前多余的数据跟现在的leader保持一致(offset)。保证数据的一致性。
5.不清洁选举
当不清洁选举成为true。不是isr的副本也可以竞选leader,当它成为leader的时候就会出现数据不一致性。followe的leo不能大于leader副本的
在这里插入图片描述
不能设置为true。
保证数据可靠性和一致性
producer:设置ack=-1
topic 设置 副本数大于3.
设置不清洁选举为false 设置unclean.leader.election.enable=false

2.kafka如何实现幂等性

2.1 幂等性解决的问题

当生产者发送消息失败重试的时候,可能是会出现重复写入。

2.2 如何操作开启kafka幂等性

开启幂等性

 pros.put('enable.idempotence',true)

在开启幂等性:
重启次数retires>0
max.in.flight.requests.per.connection<=5 飞行中请求
ack=-1
configException异常

2.2 kafka 幂等性实现机制

1.每个producer在初始化会生成一个producer_id并为每个目标partition维护一个序列号。(重试的序列号不变)
2.producer 每发送一条消息,会将<producer_id,分区> 对应的序列号+1
3.broker也是维护的序列号,每收到一条消息,会判断服务端会对新老消息的序列号对比。如果序列号不变,说明重复写入,直接丢弃,如果旧的大于新的数据。
在这里插入图片描述
producer.send(“aaa”)–aaa拥有了一个唯一的序列号
如果这条消息发送失败,produce内部重试,此时序列号不变
producer.send(“aaa”) ----aaa拥有新的序列号

3.kafka 事务

3.1 kafka 事务应用场景

实际使用是consumer->transform->produce,事务。
在这里插入图片描述

3.2 事务api

为了实现事务,程序必须提供transtional.id 并且幂等性

 pro.put("transtional.id","transtionalid001")
 pros.put('enable.idempotence',true)
 pros.setProperty(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG)

事务实验
在这里插入图片描述


import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;

import java.time.Duration;
import java.util.*;

public class KafkaTransactionDemo {

    private static final String SERVERS="dev0:9092,dev1:9092,dev2:9092";
    public static void main(String[] args) {

      //构造一个消费者去拉取数据
        Properties pros = new Properties();
        pros.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,SERVERS);
        pros.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
        pros.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        pros.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());
        pros.put(ConsumerConfig.GROUP_ID_CONFIG,"g21");
        pros.put(ConsumerConfig.CLIENT_ID_CONFIG,"c1");


        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(pros);
        consumer.subscribe(Arrays.asList("doit18"));

        //构建生产者
        Properties pros2 = new Properties();
        pros2.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        pros2.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
        pros2.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG,"txn000000001");
        pros2.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG,true);
        pros2.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,SERVERS);

        //
        KafkaProducer<String, String> producer = new KafkaProducer<>(pros2);
        //初始化事务
        producer.initTransactions();
        while(true) {
            try {


                //消费者订阅主题拉取数据
                ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));

                //获取本消费者所分配的分区
                Set<TopicPartition> assignment = consumer.assignment();


                //如果本次没有拉取到,继续下次循环
                if (records.isEmpty()) continue;
                //构造记录偏移量的hashmap 记录分区和偏移量
                HashMap<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();

                //开启事务
                producer.beginTransaction();
               //遍历每个分区,来处理分区内的数据
                for (TopicPartition topicPartition : assignment) {
                    //获取当前指定分区的数据

                    List<ConsumerRecord<String, String>> partitionRecords = records.records(topicPartition);
                    //做一些业务逻辑的处理
                    //遍历分区中每一条数据
                    for (ConsumerRecord<String, String> record : partitionRecords) {
                        //取出数据
                        String newValue = record.value().toUpperCase(Locale.ROOT);
                        //构造新的数据
                        ProducerRecord<String, String> result = new ProducerRecord<>("app_processed", record.key(), newValue);

                        //将结果写出
                        producer.send(result);
                    }
                    long lastConsumerOffset = partitionRecords.get(partitionRecords.size() - 1).offset();
                    offsets.put(topicPartition, new OffsetAndMetadata(lastConsumerOffset + 1));

                }

                //提交偏移量
                producer.sendOffsetsToTransaction(offsets, "g21");

                //提交事务
                producer.commitTransaction();
            } catch (Exception e) {
                //如果有异常放弃事务回滚
                producer.abortTransaction();
            }
        }

    }
}

4. kafka的高性能高可用原理

1.顺序写
Kafka仅仅是追加数据到文件末尾,磁盘顺序写,性能极高。
在这里插入图片描述
2.零拷贝
生产者把消息发送到KAFKA, 写入到内存,定期的刷写到磁盘。消费者在消费消息的时候,先从内存开始找,如果没有找到那么直接从磁盘把数据读到内存中。然后直接发送到网卡
在这里插入图片描述
3.日志分段存储
每个分区对应的目录,就是“topic-分区号”的格式,比如说有个topic叫做“order-topic”,那么假设他有3个分区,每个分区在一台机器上,那么3台机器上分别会有3个目录,“order-topic-0”,“order-topic-1”,“order-topic-2”。每个分区目录里面就是很多的log segment file,也就是日志段文件。
分区目录下有三类文件:
1.index --索引文件
2.log --kafka里面的数据文件,一条数据叫一个message --默认一个log文件有一个G
3.time index --时间索引文件
136371195之类的数字,就是代表了这个日志段文件里包含的起始offset
4.日志二分查找
日志段文件,.log文件会对应一个.index和.timeindex两个索引文件。而且索引文件里的数据是按照位移和时间戳升序排序的,所以kafka在查找索引的时候,会用二分查找,时间复杂度是O(logN),找到索引,就可以在.log文件里定位到数据了。

5.常见参数

在这里插入图片描述
LEO 日志末尾下一条消息
HW 高水位。isr的中最小的leo。消费者最多消费到
lso:对未完成的事务,lso是事务中第一条消息的位置。对已经完成的和hw一样
lw 低水位。ar集合最小的logstartoffset值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据学习爱好者

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值