kafka入门总结

消息队列:将生产者产出的消息放到缓冲区中,消费者从缓冲区中得到数据

  1. 应用场景:
    1. 异步:发短信
    2. 消峰:秒杀(从缓冲区中取多用户提交的数据)
    3. 解耦:flume中source和sick(用channel缓冲)
  2. 模式:
    1. 点对点(一对一):一个生产者生产的消息队列只能被一个消费者消费,消息队列不会被持久化(消费者收到消息后这条消息就从队列中删除)
    2. 发布/订阅(一对多):一个生产者生产的消息队列可以被多个消费者订阅消费,消息队列的消息会被持久化,默认保存一周

kafka在大数据中的应用场景:

  1. tomcat(logs)->flume(发送数据)->kafka -> spark streaming(分析数据)
  2. 解释:flume是主动发送数据,不断的发送,那么在spark streaming处理数据时的接收量要用kafka进行控制,否则flume发送的数据量过大产生问题,所以kafka是对flume发送的数据进行缓冲的作用,然后发送个下一阶段进行处理

架构:

  1. producer生产消息即为topic,topic进行分区,好处是并行处理
  2. 每个分区在一个broker上,即节点,每个分区是一个有序队列
  3. 每个broker上和producer联系的那个分区即为leader,每个leader都会有副本follower,且这个follower和leader在不同的节点上,leader挂了follower变成leader进行工作
  4. 副本数=leader数+follower数,且副本数不能大于节点数
  5. 分区将数据发送给消费者组,消费者组中有多个消费者,一个分区数据只能发送给一个消费者组中的一个消费者,但是一个分区数据可以发送给多个消费者组中的某个消费者

topic创建流程:

  1. client创建topic时会在zookeeper上创建一个节点,kafka上有个controller监控zookeeper上的这个节点,当节点发生变化时这个controller就能知道
  2. topic是缓存在kafka中一个metaCache的地方,metaCache在kafka每个节点都有一个

工作流程

  1. producer创建topic后,在kafka集群即每个broker上创建分区的leader和follower,producer给每个分区的leader发送数据
  2. 数据发送是批量发送,leader接收到数据后,follower去leader同步数据
  3. 消费者从分区leader消费数据
  4. 每个分区是一个有序队列,都有自己的offset,消费者消费数据的时候会记录消费的那个分区的offset,当消费者挂了重启时就会按照这个offset重新读取数据
  5. 这个offset的维护(即记录)是要消费者自己维护的,0.9版本前,offset是放到zookeeper上维护的,需要指定,这样问题是topic过多(即分区过多),那么zookeeper就会过载,0.9以后offset保存在kafka内置的一个topic中,叫__consumer__offset-*
  6. 消费者组中对topic的消费就是对每个分区的消费,那么对topic每个分区的消费的顺序是不能保证的,即全局顺序无法保证,只能保证每个分区中的有序,即分区有序

文件存储机制:

  1. 文件即topic,每个topic分成多个分区,每个分区中以log日志保存数据
  2. log日志又分成多个segment,一开始log下只有一个segment,当追加的数据超过设置的每个segment大小时就会生成新的segment,默认保存一周时间
  3. segment的存在形式是.log文件和.index文件,这样一组文件组成一个segment

log文件和index文件:

  1. 每个segment中的log名字是以这个segment的第一条消息的offset命名的,存储的是消息数据,且每个数据按照offset排列
  2. index文件命名通log文件,两列内容,一是当前segment的有序索引,二是每条消息对应的起始磁盘物理偏移量,即log文件中的数据起始偏移量
  3. 每个log和index属于一个segment,每个segment属于一个分区,每个分区是有序队列,用offset记录,每条消息的就是一个offset,所以保证不会重复
  4. 当offset=9时,先去找这个offset在哪个segment,比如每6个offset是一个segment,那么offset=9就在第二个segment,这个segment的名字中的offset是6,用9-6=3,定位第二个segment的索引3,然后对应的磁盘物理偏移量就是segment2的log文件中的数据位置

分区策略(生产者):

  1. 分区原因:
    1. 方便集群中扩展,只能增加不能减少
    2. 提高并发,提高吞吐量
  2. 分区原则:按照分区发送数据,数据封装成producerRecord对象,需要和数据有关的属性(topic名字,value值,key用来分区,指定分区等)
    1. 指明分区就按照指定的分
    2. 没有指定分区但有key,就按照key的hash值和topic的分区数取余得到的结果,将数据发到这个分区中
    3. 也没有key时,会随机生成一个整数(以后自增),这个数和分区数取余得到的数即为数据发往的分区数,即round-robin
    4. 默认round-robin模式

数据可靠性保证:保证数据从生产者到kafka,再从kafka到消费者的过程中不丢失

  1. 生产者往分区的leader(kafka集群broker)发送消息,分区收到数据后给生产者响应收到(ack)即完成,若没有响应收到,生产者重新发送消息
  2. leader收到消息后,follower会和leader同步消息
  3. ack发送时间:
    1. leader收到消息后
    2. follower和leader同步消息后:数据已备份,可以保证leader挂掉后,能重新选举出leader,数据不会丢失。follower同步两种模式:
      1. follower半数以上同步后:快,但需要2n+1的副本容错
      2. follower全部同步后:慢,但需要n+1的副本容错
      3. 结论:kafka存的是真实的数据,多一个副本就多存一份数据,大量数据时副本数多必会加大存储空间,及增大数据冗余,另外kafka高效读写数据(顺序写,零复制)技术可以加快速度,所以用第二种
    3. 说明:kafka的ack模式有三种,以上结论说的是第三种
    4. 当ack为-1时,leader要等所有follower返回信息后才会给producer发送ack,那么若有个follower挂掉或数据量很大迟迟没有给leader返回信息怎么办?(ISR动态集合中的follower同步完数据就发送ack)
    5. ISR:
      1. 用于leader长时间等待follower同步数据的问题和重新选举leader。
      2. ISR是一个动态保存follower的集合,每个分区中都有一个,leader是和ISR中的follower进行交互的,比如选举新leader。
      3. 若follower因某种原因同步时间过长,ISR就将这个follower踢出ISR,参数是一个时间参数replica.lag.time.max.ms,旧版本还有个落后条数的参数,但不合理,因为若是数据量太大,此follower就会产生落后条数过大的情况,当踢出后恢复重新进入ISR,还有可能再被踢出,这样反复消耗资源。
      4. 踢出的follower是临时踢出,当follower恢复并和leader同步完数据后,此follower将再次进入ISR
  4. 以上只是保证数据的不会丢失,并没有保证数据不会重复
  5. ack三种模式:
    1. acks=1:生产者发送消息,leader接收到就发送ack,若follower没同步完leader挂掉,重新选出的leader并没有新数据,造成数据丢失
    2. acks=0:生产者发送消息即获得ack,数据丢失
    3. acks=-1(all):follower都同步完成后,发送ack之前leader挂掉,会造成producer重新发送数据,数据重复
  6. 出现故障后kafka数据如何处理(即保持一致):
    1. LEO:leader或follower最后的offset,副本各自不会相同
    2. HW:所有leader和follower中,那个Leo和hw的offset相等的位置即为所有follower和leader共同的HW,每个副本的HW一样
    3. 所有follower和leader通信,告诉leader自己的LEO,leader选出最小的Leo即为HW,然后将这个HW发给其他follower,HW存在各自磁盘上
    4. follower故障:被踢出ISR恢复后,去和leader同步,同步位置以自己记录的HW为准,HW后面到Leo的部分截取调,当从HW位置同步到leader的Leo后,即完成与leader的同步,重新进入ISR
    5. leader故障:选举出新的leader,其余follower将从自己的HW位置去掉后面的数据,然后和新leader同步
    6. 以上只保证数据的一致性,不保证不重复或不丢失,数据的丢失或者重复时靠ack决定的
  7. 消息传递可靠性语义:
    1. at most once:至多有一次,可能丢失
    2. at least once:至少有一次,可能重复
    3. exactly once:有且只有一次。0.11版本后,加入幂等性机制(只序列化一次),配合acks=-1,就可以做到有且只有一次。将一个属性enable.idempotence设置为true,不用设置acks=-1了
    4. 幂等性机制:给消息加上唯一id。producerId+topicId,此消息写入kafka时将此id缓存,当acks=-1且没有收到ack时producer会再次发送消息,那么会再次将这个id带上发送到kafka,kafka从缓存中见到同样的id就不会保存

消费者:

  1. 消费方式:
    1. kafka是拉的方式:可以根据消费者消费能力消费消息。当kafka没有消息时,会循环拉取数据,针对这点,消费者消费数据时会传入一个时长参数timeout,若没有数据会等待timeout时间再返回,默认100毫秒
    2. push方式:不考虑消费者消费速率,只是最快传递消息
  2. 分区分配原则:将分区分配给消费者的原则
    1. round-robin:轮寻方式将分区分给消费者
    2. range:默认方式。多个topic时,每个消费者所分配到的分区会不均
    3. stick:0.11版后

高效读写数据:会持久化磁盘

  1. 顺序写:producer生产数据写入log时,是将数据追加的文件末尾,即顺序写。这样写省去磁头寻址时间,速度非常快
  2. 零复制技术:传统读取发送文件过程为 文件->内核->用户->内核->发送。kafka发送文件方式 文件->内核->发送,少去了和用户层的交互,因为生产者生产的数据就复合网络发送的协议,不需要用户层的解码等处理

zookeeper在kafka中的作用:

  1. kafka中的broker会有一个作为controller,负责管理所有broker和分区副本分配及leader选举等
  2. zookeeper辅助这个controller去完成以上工作
  3. 启动kafka时,每个broker上都会有个controller实例,但只有先到达zookeeper注册成功的那个才会成为最终唯一的那个controller
  4. 举例:leader选举流程(leader第一次是分配的,manager中preferred那个就是)
    1. kafka集群启动时会选举出kafkaController,并且在zookeeper的/brokers/ids上注册每个broker的临时节点(即broker挂了节点id就没了),kafkaController会监听这个临时节点
    2. 生产者创建一个topic,会在zookeeper上的/brokers/topics/topicName/partitions/index/state注册,注册内容会有leader在那个broker上,和ISR信息
    3. 当leader挂了后,zookeeper上的临时节点中的id也会消失,监控这个临时节点的kafkaController就会知道,然后从topic在zookeeper上的注册信息中得到ISR信息,并按照顺序将下一个brokerid上的副本编程leader,并更新这个注册信息
    4. kafkaController是通过每个broker上的metaCache得知broker上的leader信息的
    5. 当kafkaController挂掉后,其它broker上的Controller会再去zookeeper注册,谁先注册上谁就成为新的kafkaController

producer发送消息流程:

  1. Producer->send(ProducerRecord)->Interceptors->Serialier->Partitioner->RecordAccumulator->Sender
  2. 分区器往RecordAccumulator上批量发送数据,根据大小batch.size和时间linger.ms批量发

Producer API:主要是发送消息,同步发送,异步发送,新版本的同步发送是在异步发送中调用future.get()方法实现的

Consumer API:主要是对offset的手动和自动维护。offset可以放到外部数据库中,这样做的目的是保证offset原子性(即完成数据的消费和offset的提交在一个事务中)

  1. 同步提交会一致提交offset,直到成功为止
  2. 异步提交只提交一次offset,使用异步提交,一批提交一次offset,若这次没成功下次提交可以带上上一批提交

数据消费重复问题:

        比如已经消费了0、1、2,consumer记录的offset是3,那么再消费3、4、5,此时没有提交offset就挂了,当再次通过offset=3读取数据是3、4、5,此时读的数据就重复了。此时就需要保证数据的消费和offset的提交再一个事务中,这样才能保证数据的原子性,即exactly once

重新分配分区:

        消费者订阅topic方法中可以传递重新分配分区的属性,里面两个方法,一个是回收的分区,一个是重新分配的分区,当有第二个消费者消费同一个topic且属于同一个消费者组时,就会将所有分区进行回收和重新分配

  1. 那么就要在这两个方法中,回收分区的方法中将offset进行提交,因为自定义了commit方法,是实时提交的,所以这个方法里不用再写offset提交代码了
  2. 在重新分配的方法中定位新分配的分区的offset:自定义方法获取offset,调用consumer.seek()方法定位offset。自定义commit方法
  3. 最终保证消息的消费和commit提交在一个事务里就保证了exactly once

拦截器:

  1. 有两个方法:
    1. onSend:在producer的send方法中调用,运行在主线程,在序列化和分区前调用
    2. onAcknowledgement:在producer的callback调用前调用,或者消息成功或失败发送到broker之后调用,运行在IO线程中,会拖慢速度
    3. 以上两个方法运行在不同的线程中,但有共享变量或资源时会产生线程安全问题
  2. 拦截器可以对数据进行简单的预处理,发送前处理和回调前处理下

kafka manager:

  1. preferred 副本election:初始化kafka时均匀分配的leader分区计划(当leader挂掉会造成leader分配不均,那么这个按钮会按照初始化时的均匀分配进行重新分配,达到再次平均分配leader所在分区的目的)
  2. reassign partitions:重新分配分区
  3. 数据倾斜或者某个分区数据很大,其它分区很小时才去重新分配分区,重新分区时数据会大量迁移

kafka项目中应用:

  1. 通过flume将监控的文件传给kafka,也就是flume的source是某个地方的文件,sink是kafka
  2. 在这个kafka sink中要指定kafka的topic,那么flume就会把文件传给这个topic的kafka了
  3. 在kafka创建(或已经有)这个topic,然后打开kafka监控,再然后只要这个flume运行,kafka就会自动监控这个文件了,因为flume已经把这个文件传给kafka了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值