Kafka从入门到精通(一)

22 篇文章 0 订阅

一,首先讲kafka的两种模式 

  1,点对点模式,也就是poll模式,消费者主动拉取数据,消息收到后清除消息

   2,发布,订阅模式:

  •  可以有多个topic
  • 消费者消费数据后,不删除数据
  • 每个消费者相互独立,都可以消费到数据(同一条消息可以被不同的消费者同时消费)

 

kafka的基础架构(瓶颈:当存储的数据量过大时,一台broker很难承受太大的压力,这个时候就需要搭建集群,引入分区) 

 kafka集群架构(优点:1.对消息进行分区管理,减少压力,2.消费者也要搭建集群,分组,3.多个分区可以被同一个分组消费,但是同一个分区,只能被一个分组消费,4.在每一个broker中存的分区可以建立副本,就可以保证当一台broker宕机后,他的副本可以升为leader继续进行消息存储5.这里提到了zookeeper,在kafka里存的就是哪个broker在运行,以及以及每个分区当前谁是leader)

zookeeper和kafka的安装和启动(需要先安装jdk,这里不做概述)

zookeeper的安装和启动

①:到apache上的zookeeper网站:Apache ZooKeeper

②:选择Getting Started下的Download点击进入:

 ③选择Download下的Download

④选择清华大学的站点地址:Index of /apache/zookeeper 

 

⑤三者选择其中之一,然后进行下载:

 

2、zookeeper的安装:

①:将下载的zookeeper安装包上传到Linux中的/usr/local目录下

②:在/usr/local下新建一个software文件夹,专门用来存放安装的软件

③:解压 tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz

④:mv apache-zookeeper-3.5.5-bin software (将apache-zookeeper-3.5.5-bin移动到software文件夹下)

3、zookeeper的配置:

⑤:在/usr/local/software/apache-zookeeper-3.5.5-bin下创建data、logs文件夹

⑥:cd /usr/local/software/apache-zookeeper-3.5.5-bin/conf,然后cp zoo_sample.cfg zoo.cfg(复制创建一份新的zoo.cfg配置文件)

⑦:vim zoo.cfg将dataDir=/tmp/zookeeper  改为:dataDir=/usr/local/software/apache-zookeeper-3.5.5-bin/data 

4、启动:

在/usr/local/software/apache-zookeeper-3.5.5-bin下 bin/zkServer.sh start启动

5、停止:

在/usr/local/software/apache-zookeeper-3.5.5-bin下 bin/zkServer.sh stop 停止

Kafka的下载、安装、配置

1、kafka的下载:

①:进入Apache Kafka  ,选择Download

②:选择版本下载:

2、安装

①:将kafka_2.12-2.3.0.tgz上传到/usr/local下

②:解压 tar -zxvf kafka_2.12-2.3.0.tgz

3、配置

在/usr/local/software/kafka_2.12-2.3.0下 运行 vim config/server.properties(编辑server.properties文件)

  • log.dirs=/usr/local/EntyApp/env/app/kafka_2.13-3.0.0/data //kafka的数据存放地址,必须修改,放在kafka的data目录下

  • zookeeper.connect=192.168.159.143:2181/kafka //kafka连接zookeeper的地址,后边必须加kafka,否则在kafka关机后,存放在zookeeper的数据会散落到各个地方,需要手动清除

kafka后台启动的命令:

bin/kafka-server-start.sh -daemon config/server.properties

到这里,kafka就可以使用了

下边来讲一讲kafka生产者发送到broker的过程:

 消息生产到发送到broker的流程:

  1. 首先生产者main线程会创建一个producter消息生产者用来发送消息,然后可以配置拦截器,根据业务场景,决定是否需要,然后会经过序列化器,使用的是kafka自己的序列化器,java的序列化器太重,消息内容繁琐且占用内存大,所以kafka使用的是自己的序列化器,然后会经过分区器,分区器根据你的分区编号来分区
  2. 分区器根据分区数先在内存中决定要发送到哪个区,为每一个分区创建一个队列,用来存储消息,整个内存消息区一共为32m,每个区为16k为满,当一个区满时或者因为长时间未满,但是达到了消息存储最长时间,这个时候sender线程就出来了(这里的分配区,实际上是一个内存池,当消息内存释放后,会归还给内存池,实现了复用)
  3. sender线程会把满的队列,或者超时的队列先分配一下,具体发送给哪个broker,每个broker对应多个请求,假如给broker1发送了五个请求,都未应答,则下一个请求就不会往broker1发了,直到,一开始发送的五个请求有了相应,才会继续往该broker发送消息,请求发送和接受使用的是一个selector模型,若收到的响应为成功,则会把sender中的请求清除掉,把分区器分配的该消息占用的内存归还给内存池,如果失败,则会重试发送,重试的次数是int的最大值,这个次数可以自己配置
  4. 接下来当请求到达broker集群后,若是成功放入了分区,kafka会有一个节点副本同步机制,会把当前节点的数据同步给副本,而kafka给sender的响应时机有三种,第一种就是无需等待应答,sender接着发送,第二种就是,leader收到消息,就响应,第三种就是,整个集群每个副本都同步成功了,才响应

下边来介绍消息生产发送到broker的一些配置

分区策略

那么具体在生产环境中如何设置

ProducerRecord的第二个参数就是分区数,但是必须上传key,我们可以设置为空
kafkaProducer.send(new ProducerRecord<>("first", 1,"","hello" + i), new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {

                    if (exception == null){
                        System.out.println("主题: "+metadata.topic() + " 分区: "+ metadata.partition());
                    }
                }
            });

分区呢? 

如何提高kafka消息发送的吞吐量呢?

无非就是三个参数,分区队列大小,最长消息存放时间,还有就是整个分区缓冲区大小,一般我们生产环境,存放时间就是5-100ms,队列大小为16k,缓冲区根据实际分区数量来定,一般32m够的

 // 0 配置
        Properties properties = new Properties();

        // 连接kafka集群
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop102:9092,hadoop103:9092");

        // 序列化
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());

        // 缓冲区大小
        properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG,33554432);

        // 批次大小
        properties.put(ProducerConfig.BATCH_SIZE_CONFIG,16384);

        // linger.ms
        properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);

        // 压缩
        properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"snappy");


        // 1 创建生产者
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        // 2 发送数据
        for (int i = 0; i < 5; i++) {
            kafkaProducer.send(new ProducerRecord<>("first","atguigu"+i));
        }

        // 3 关闭资源
        kafkaProducer.close();

如何保证数据的可靠性?不丢失且不重复消息

消息不丢失:

首先了解一下kafka消息落盘的响应机制

那么最终保证可靠性的方案是什么呢?

至少成功一次:ask级别设置为-1+加分区副本大于等于2+ISR队列最小副本数量大于等于2(ISR是kafka为了保证follow可以正常向leader同步,如果挂了,那么它在一段时间未向leader发出同步请求,就会被踢出ISR队列) 

设置如下:

// acks
        properties.put(ProducerConfig.ACKS_CONFIG,"1");

        // 重试次数
        properties.put(ProducerConfig.RETRIES_CONFIG,3);

she性:上边的方法其实已经很可靠了,但是还有问题,比如leader消息已经落盘了,并且同步好了副本节点,还没有响应,这个时候leader宕机了,那么这次发过来的消息,sender现场会认为失败了,会重复发送,这样就造成了消息重复

 要保证单会话的可靠性,其实比较简单,就是使用幂等性,它判断重复的三个主要元素是:

pid(kafka每次重启都会重新生成一个)

partition(分区数)

seqNumber(每个分区消息单调递增)

开启这个参数即可,默认就是开启的:enable.idempotence=true

但是如果kafka节点宕机了,重新启动,还是会有重复的问题,那么到这只能使用事务来保证消息不重复了

 // 指定事务id
        properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "tranactional_id_01");

        // 1 创建kafka生产者对象
        // "" hello
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        kafkaProducer.initTransactions();

        kafkaProducer.beginTransaction();

        try {
            // 2 发送数据
            for (int i = 0; i < 5; i++) {
                kafkaProducer.send(new ProducerRecord<>("first", "atguigu" + i));
            }

            int i = 1 / 0;

            kafkaProducer.commitTransaction();
        } catch (Exception e) {
            kafkaProducer.abortTransaction();
        } finally {
            // 3 关闭资源
            kafkaProducer.close();
        }

切记,一定要指定事务id,负责会失败

数据有序:就是消费者,要按照生产者发送消息的顺序,进行消费

分区broker:这个需求,单节点,好解决,后边再讲,先说多节点,那么唯一的方法就是,消费者把整个sender线程的所有请求全部拿过来,然后再排序,再进行消费,那这样的话,效率自然就会比较低

单机kafka:  

 每次sender的每个分区最多缓存五个请求,只有全部发送成功,才能再次发送,等消息成功发送到kafka并成功落盘后,会对发过来的数据重新进行排序,这样就保证了消息消费的一个顺序

zookeeper中存储kafka的哪些信息:只列举几个重要的部分

  • /kafka/brokers/ids 记录kafka集群节点,存活的
  • /kafka/brokers/topics/first/partitions/0/state 记录每个topic每个分区的leader和follow信息
  • /kafka/controller 用于注册节点controller,辅助选举leader

kafka中是如何存储数据信息的?

kafka会把每个节点每个分区的数据,采取分片和索引机制,将每个partition分为多个segment。每个segment大小为一个g,包括:".index 文件" ".log 文件" " .timeindex 文件",这些文件位于一个文件夹下,该文件夹的命名规则为topic名称+分区序号,例如first-0

index是如何存储的:

log文件每写入4kb文件就往index文件中写入一条信息,内容为offset(相对当前log起始offset偏移量)和position,采用稀疏索引的方式,当需要找到某个offset的具体位置时,可以先到index文件中找到对应的position位置,再通过position到log文件找到对应的日志消息数据

如何查看segment下的文件呢?

kafka-run-class.sh kafka.tools.DumpLogSegments --files ./0000000000000000.log

kafka分区leader和follower选举策略,完整步骤

 首先,每个kafka节点启动先向zookeeper注册节点信息,同时每个节点的controller也向zookeeper的controller注册节点信息,谁第一个注册上去,就由,哪个节点的controller 来负责leader选举,假如broker0的controller为选举者,那么,先从注册列表中,拿出ar注册顺序,然后看isr存活的服务节点,首先存活,然后按照ar顺序选举,谁在第一个就是leader,然后broker0的controller会把分区节点信息放入zookeeper的/kafka/brokers/topics/first/partitions中,然后副本向zookeeper申请同步相关信息,假如选举者broker0挂了,他们可以,及时补充上去,假如leader挂了,那么选举者就会检测到ids服务列表的变化,及时拿到新的节点信息,马上从zookeeper拿到之前的节点信息,按照ar顺序,且在isr列表中的副本中进行选举,到这里就是完整选举步骤了

服务加入新节点:假如希望新添加一个kafka服务器,用来存储副本信息,以减少分区服务器的压力

  1. 创建一个想要增加节点负载均衡的topic的json文件
vim topics-to-move.json

{
"topics": [
{"topic":"first"}]

"version": 1}

   2.生成一个负载均衡计划

 bin/kafka-reassign-partitions.sh --bootstrap-server kafka节点ip:port --topics-to-move-json-file 上一步生成文件绝对路径 --broker-list "集群几点brokerid,逗号隔开" --generate

//执行完上边的操作,会生成一个执行计划

  3.将上边的执行计划放入一个increase-replication-factor.json文件中

   4.执行存储计划

bin/kafka-reassign-partitions.sh  --bootstrap-server
hadoop192:9092  --reassianment-ison-file increase-replication-factor.json --execute



 

  5.查看执行结果

bin/kafka-reassign-partitions.sh  --bootstrap-server
hadoop192:9092  --reassianment-ison-file increase-replication-factor.json --verify

退役旧节点

和新增节点操作一样

follower节点挂了底层做了什么

首先明白两个概念:

LEO(log end offset) :每个节点分区的消息offset下标+1

HW(High Watermark):当前分区的leader和follower中最小的LEO

首先会把follower从isr队列中移除,然后正常的leader和follower继续接收数据,等挂掉的follower恢复后,会按照它挂时候hw把后边发过来的消息扔掉,它认为这是没有验证的数据,不安全,然后从这个hw位置向leader申请数据同步,等该follower的hw追上正常的leader和follower的hw时,就是大于等于,就可以重新加入ISR队列了

如果只有一个broker是否还有必要分区?

首先要明白分区的目的是什么?

  1. 容量扩展:多个分区允许你以分布式的方式扩展Kafka集群的容量。当你的数据量增长时,你可以增加更多的broker,并为每个broker分配分区,以增加整体的处理能力。

  2. 并行消费:即使在单个broker上,多个消费者也可以并行消费多个分区的消息。这样可以提高消费速度,并使多个消费者能够处理更大的消息流。

  3. 容错性:使用多个分区可以提高系统的容错性。如果某个分区发生故障,其他分区仍然可以正常运行,确保系统的可用性。

  4. 负载均衡:多个分区可以实现负载均衡,将消息均匀地分布到不同的分区中。这样可以避免某个分区成为瓶颈,影响整体系统的性能。

因此,即使只有一个broker,使用多个分区仍然有助于提高Kafka集群的可扩展性、性能和容错性。然而,具体的分区设置应该根据你的应用需求和数据流量来确定,以确保达到最佳的平衡。

如何修改kafka分区数据的存储位置

  1. 先创建一个increase-replication-factor.json文件,在里边加入一下内容
//这里就是将三个分区的数据全部存到0,1主机上
{
  "version": 1,
  "partitions": [
    {
      "topic": "topic1",
      "partitions": 0,
      "repalicas": [
        1,
        0
      ]
    },
    {
      "topic": "topic1",
      "partitions": 1,
      "repalicas": [
        1,
        0
      ]
    },
    {
      "topic": "topic1",
      "partitions": 2,
      "repalicas": [
        1,
        0
      ]
    }
  ]
}

2.执行文件

bin/kafka-reassign-partitions.sh  --bootstrap-server
hadoop192:9092  --reassianment-ison-file increase-replication-factor.json --execute

3.查看执行结果

bin/kafka-reassign-partitions.sh  --bootstrap-server
hadoop192:9092  --reassianment-ison-file increase-replication-factor.json --verify

4.查看分区副本存储情况

bin/kafka-topics.sh  --bootstrap-server
hadoop192:9092  --describe --topic three

leader Partition负载均衡,自动平衡

当多个broker集群其中部分挂了以后,会将其他主机上的副本升为主机,这样就会造成一台主机上好几个分区的leader同时操作,大家知道不管消费者还是生产者,操作的对象始终是leader,那这样的话他们的压力就会很大

在kafka中有这样一个参数auto.leader.rebalance.enable可以设置是否开启自动均衡,默认就是true,但是生产环境不建议开启,要关闭,可以调整kafka数据存储位置的方式手动调整

kafka的segment文件清理策略

log.cleanup.policy=delete/compact默认是删除,时间为7天删除一次,可通过log.retention.hours设置小时,然后检测周期是默认5分钟,可通过log.retention.check.interval.ms设置值

kafka高效读写数据的原理

1.采用分区技术,可并行操作

2.读取数据采用稀疏索引,可快速定位到要消费的数据

3.按顺序追加数据,会按顺序往log文件中追加信息

4.kafka服务端不处理数据,实现了零拷贝,数据由生产者发过服务端以后,会先缓存在page cache中,由操作系统控制持久化到硬盘里,再需要消费时,先从缓存找,没找到再去硬盘中找,然后不需要返回给kafka服务端,直接通过网卡传输给消费者,由消费者根据自己的需求做特殊处理,kafka服务端只负责存取的职责

kafka消费者组讲解

如果消费者组大于分区数,消费者如何消费对应的分区?

如果是这样的话,多出来的消费者就会闲着

消费者组初始化以及分区的分配流程?

 首先来讲一下coordinator,这个东西,是干嘛的呢,首先每个broker节点都会有自己的coordinator,根据消费者组id的hash%50取余数,得到的数字,就是选举的消费者组对应的coorfinator的brokerid,有它负责对接消费者组,那么选举出了消费者组领导人,消费者组每个消费者就会向这个coorfinator发一个加入消费者组请求,然后这个coorfinator会随机选出一个消费者作为leader,然后coorfinator把需要消费的topic信息发给这个leader,由它指定消费方案,发送给coorfinator,它会把具体的消费方案下发给具体的消费者

消费者具体的消费流程?

 

上个问题解决了分配流程,接下来,每个消费者组拿到了具体的消费方案,开始从kafka消费,首先消费者要向和broker建立连接开始消费,需要有一个consumerNetworkClient网络连接客户端,设置一些拉取参数,然后发送给broker,broker收到拉取请求后,就会调用对应的一个completedFetches成功的回调函数来拿到对应的消息,然后存在一个队列里,再经过反序列化器和拦截器,最终到达消费者,开始处理数据

consumer消费者组leader分配消费方案的策略有几种?

range:

 

roundRobin:

 sticty:

和range在分配数量上是一样的策略,但是,分配的分区是随机的

消费者的消费的offset下标是存在哪里的?

在kafka0.9之前是存在zookeeper中的,但是这样,每次消费都要消耗大量的网络开销,会影响性能,于是在0.9之后,全部放在了kafka主机文件上

如何查看offset的值呢?

  1. 首先在config/consumer.properties中添加一行exclude.internal.topics=false
  2. 执行如下命令
    bin/kafka-console-consumer.sh --topic consumer offsets --bootstrap-server hadoop102:9092   --consumer.config config/consumer.properties --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter"--from-beginning

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EntyIU

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值