大数据常见面试题之kafka

一.kafka和传统消息队列区别

  • 首先kafka会将接收到的消息分区(partition),每个主题(topic)的消息有不同的分区,这样一方面消息的存储就不会受到单一服务器存储空间大小的限制,另一方面消息的处理也可以在多个服务器上并行
  • 其次为了保证高可用,每个分区都有一定数量的副本,这样如果有部分服务器不可用,副本所在的服务器就会接替上来,保证应用的持续性
  • 另外kafka保证分区内部消息的消费有序性
  • kafka还具有consumer group的概念,每个分区只能被同一个group的一个consumer消费,但可以被多个group消费

和RabbitMQ进行对比:

  • 1.架构模型方面
  • RabbitMQ遵循AMQP协议,RabbitMQ的brokerExchange,Binding,queue组成,其中exchange和binding组成了消息的路由键;客户端Producer通过连接channel和server进行通信,consumer从queue获取消息进行消费(长连接,queue有消息会推送到consumer端,consumer循环从输入流读取数据).rabbitMQ以broker为中心:有消息的确认机制
  • kafka遵从一般的MQ结构,producer,broker,consumer根据消费的点,从broker上批量pull数据,无消息确认机制
  • 2.吞吐量
  • kafka具有高的吞吐量,内部采用消息的批量处理,zero-copy机制,数据的存储和获取是本地磁盘顺序批量操作,具有O(1)的复杂度,消息处理的效率高
  • rabbitMQ在吞吐量方面稍逊于kafka,他们的出发点不一样,rabbitMQ支持对消息的可靠的传递,支持事务,不支持批量操作.基于存储的可靠性的要求存储可以采用内存或者硬盘
  • 3.可用性
  • rabbitMQ支持mirror的queue,主queue失效,mirror queue接管
  • kafka的broker支持主备模式
  • 4.集群负载均衡
  • kafka采用zookeeper对集群中的broker,consumer进行管理,可以注册topic到zookeeper上,通过zookeeper的协调机制,producer保存对应topic的broker信息,可以随机或者轮询发送到broker上,并且producer可以基于语义指定分片,消息发送到broker的某分片上

二.kafka的应用场景

  • kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据.简单地说,kafka就好比是一个邮箱,生产者就是发送邮件的人,消费者就是接收邮件的人,kafka就是用来存东西的,只不过它提供了一些处理邮件的机制,使用场景包括:
  • 日志收集:一个公司可以用kafka收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer
  • 消息系统:解耦生产者和消费者,缓存消息等
  • 用户活动跟踪:kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页,搜索,点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后消费者通过订阅这些topic来做实时的监控分析,亦可保存到数据库
  • 运营指标:kafka也经常用来记录运营监控数据,包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告
  • 流式处理:比如sparkstreaming和storm

三.kafka在高并发的情况下,如何避免消息丢失和消息重复

1.消息丢失解决方案

  • 1)对kafka进行限速
  • 2)启用重试机制,重试间隔时间设置长一些
  • 3)kafak设置acks=all,即需要相应的所有处于ISR的分区都确认收到该消息后,才算发送成功

2.消息重复解决方案

  • 1)消息可以使用唯一id标识
  • 2)生产者(ack=all代表至少成功发送一次)
  • 3)消费者(offset手动提交,业务逻辑成功处理后提交offset)
  • 4)落表(主键或者唯一索引的方式,避免重复数据)
  • 业务逻辑处理(选择唯一主键存储到Redis或者mongdb中,先查询是否存在,若存在则不处理;若不存在,先插入Redis或者Mongdb,在进行业务逻辑处理)

四. kafka到sparkstreaming怎么保证数据完整性,怎么保证数据不重复消费?

保证数据不丢失(at-least)

  • spark rdd内部机制可以保证数据at-least语义
  • Receiver方式.开启WAL(预写日志),将从kafka中接收到的数据写入到日志文件中,所有数据从失败中可以恢复

Direct方式:

  • a.依靠checkpoint机制来保证
  • b.要保证数据不重复,即Exactly once语义
  • 幂等操作:重复执行不会产生问题,不需要做额外的工作即可保证数据不重复
  • 业务代码添加事务操作,就是说针对每个partition的数据,产生一个uniqueld,只有这个partition的所有数据被完全消费,则算成功,否则算失效,要回滚.下次重复执行这个uniqueld时,如果已经被执行成功,则skip掉

五.kafka的消费者高阶和低阶API区别

  • kafka提供了两套consumer API:The high-level Consumer API 和The SimpleConsumer API,其中high-level consumer API 提供了一个从kafka消费数据的高层抽象,而SimpleConsumer API 则需要开发人员更多地关注细节

1.The high-level Consumer API

  • high-level Consumer API 提供了consumer group地语义,一个消息只能被group内地一个consumer所消费,且consumer消费消息时不关注offset,最后一个offset由zookeeper保存
  • 使用 high-level consumer API可以是多线程地应用,应当注意:
  • 1)如果消费者线程数大于partition数,则有些线程收不到消息
  • 2)如果partition数量大于线程数,则有些线程收到多个partition的消息
  • 3)如果一个线程消费多个partition,则无法保证你收到的消息的顺序,而一个partition内的消息是有序的

2.The SimpleConsumer API

  • 如果你想要对partition有更多的控制权,那就应该使用SimpleConsumer API 比如:
  • 1)多次读取一个消息
  • 2)只消费一个partition中的部分消息
  • 3)使用事务来保证一个消息仅被消费一次
  • 但是使用此API时,partition offset broker leader 等对你不再透明,需要自己去管理,你需要做大量的额外工作
  • 必须在应用程序中跟踪 offset,从而确定下一条应该消费哪条消息
  • 应用程序需要通过程序获知每个partition的leader是谁
  • 需要处理leader的变更

六.kafka怎么保证数据消费一次且仅消费一次

  • 幂等producer:保证发送单个分区的消息只会发送一次,不会出现重复消息
  • 事务(transaction):保证原子性地写入到多个分区,即写入到多个分区地消息要么全部成功,要么全部回滚

七.kafka保证数据一致性和可靠性

1.数据一致性保证

  • 一致性定义:若某条消息对client可见,那么即使leader挂了,在新leader上数据依然可以被读到
  • HW-HighWaterMark:client可以从leader读到的最大msg offset,即使对外可见的最大offset,HW=max(replica.offset)
  • 对于leader新收到的msg,client不能立刻消费,leader会等待该消息被所有ISR中的replica同步后,更新HW,此时该消息才能被消费,这样就保证了如果leader fail,该消息仍然可以从新选举的leader中获取
  • 对于来自内部Broker的读取请求,没有HW的限制,同时,follower也会维护一份自己的HW, Follower.HW = min(Leader.HW,Follower.offset)

2.数据可靠性保证

  • 当producer向leader发送数据时,可以通过acks参数设置数据可靠性的级别
  • 0: 不论写入是否成功,server不需要给producer发送response,如果发生异常,server会终止连接,触发producer更新meta数据
  • 1: leader写入成功后即发送response,此种情况如果leader fail,会丢失数据
  • -1: 等待所有ISR接收到消息后再给producer发送response,这是最强保证

八.spark实时作业宕掉,kafka指定的topic数据堆积怎么办

  • 1.spark.streaming.concurrentJobs=10:提高Job并发数,从源码中可以察觉到,这个参数其实是指定了一个线程池的核心线程数而已,没有指定时,默认为1
  • 2.spark.streaming.kafka.maxRatePerPartition=2000:设置每秒每个分区最大获取日志数,控制处理数据量,保证数据均匀处理
  • 3.spark.streaming.kafka.maxRetries=50:获取topic分区leaders及其offsets时,调大重试次数
  • 4.在应用级别配置重试. spark.yarn.maxAttemps=5 不能超过hadoop集群中yarn.resourcemanager.am.max-attempts
  • 5.尝试失败有效间隔时间设置. spark.yarn.am.attempFailuresValidtyInterval=1h

九.kafka读写流程

1.写流程

  • 1)连接zk集群,从zk中拿到对应topic的partition信息和partition的leader的相关
  • 2)连接到对应leader对应的broker
  • 3)将消息发送到partition的leader上
  • 4)其他follower从leader上复制数据
  • 5)依次返回ack
  • 6)直到所有ISR中的数据写完成,才完成提交,整个写过程结束
  • 因为是描述写流程,没有将replica与zk的心跳通讯表达出来,心跳通讯就是为了保证kafka高可用.一旦leader挂了,或者follower同步超时或者同步过慢,都会通过心跳信息报告给zk,由zk做leader选举或者将follower从ISR中移到OSR中

2.读流程

  • 1)连接zk集群,从zk中拿到对应topic的partition信息和partition的leader的相关
  • 2)连接到对应leader对应的broker
  • 3)consumer将自己保存的offset发送给leader
  • 4)leader根据offset等信息定位到segment(索引文件和日志文件)
  • 5)根据索引文件中的内容,定位到日志文件中该偏移量对应的开始位置读取相应长度的数据并返回给consumer

十.kafka为什么只让leader进行读写

  • kafka只有leader负责读写,follower只负责备份,如果leader宕机的话,kafka动态维护了一个同步状态的副本的集合(a set of in-sync replicas),简称ISR,ISR中有f+1个节点,就可以允许在f个节点down掉的情况下不会丢失消息并正常提供服务. ISR的成员是动态的,如果一个节点被淘汰了.当它重新达到"同步中"的状态时,他可以重新加入ISR.因此如果leader宕了,直接从ISR中选择一个follower就行
  • kafka在引入replication之后,同一个partition可能会有多个replica,而这时需要在这些replication之间选出一个leader,producer和consumer只与这个leader交互,其他replica作为follower从leader中复制数据,因为需要保证同一个partition的多个replica之间的数据一致性(其中一个宕机后其他replica必须要能继续服务并且既不能造成数据重复也不能造成数据丢失). 如果没有一个leader,所有replica都可同时读/写数据,那就需要保证多个replica之间互相(n*n条通路)同步数据,数据的一致性和有序性非常难保证,大大增加了replication实现的复杂性,同时也增加了出现异常的几率.而引入leader后,只有leader负责读写数据,follower只向leader顺序fetch数据(n条通路),系统更加简单且高效

十一.为了避免磁盘被占满,kafka会周期性的删除旧消息,请问删除策略有哪些?

  • kafka中有两种保留策略
  • 一种是根据消息保留的时间,当消息在kafka中保存的时间超过了指定时间,就可以被删除
  • 另一种是根据topic存储的数据大小,当topic所占的日志文件大小大于一个阈值,则可以开始删除最旧的消息
  • kafka会启动一个后台线程,定期检查是否存在可以删除的消息
  • 保留策略的配置是非常灵活的,可以有全局的配置,也可以针对topic进行配置覆盖全局配置

十二.kafka数据高可用的原理

1.数据存储格式

  • kafka的高可靠性的保障来源于其健壮的副本策略. 一个topic可以分成多个partition,而一个partition物理上由多个segment组成
  • segment分2部分:索引文件和数据文件. 索引文件保存元数据,记录了消息在数据文件中的偏移量(offset),消息有固定物理结构,保证了正确的读取长度
  • segement文件带来的好处:方便过期文件清理.只需要整体删除过期的segment.以追加的方式写消息,顺序写磁盘极大提高了效率
  • 读取某offset消息的步骤变为:通过二分查找,找到offset所在的segment.通过segment的索引文件,找到offset所在数据文件的物理偏移,读取数据

2.副本复制与同步

  • 从外部来看partition类似一个不断增长,存储消息的数组,每个partition有一个类似MySQL binlog的文件来记录数据的写入. 有两个新名词,HW(HighWatermark)表示当前consumer可以看到partition的offset位置,LEO(LogEndOffset)表示当前partition最新消息的offset,各个副本单独维护. 为了提高消息可靠性,partition有n个副本
  • n个副本中,有一个leader,余下n-1个follower. kafka的写操作只在leader副本上进行. 通常这种副本写有两种方式
  • 1)leader写日志文件成功即返回成功. 这样如果follower在同步完数据前leader宕机,数据丢失.这种方式带来较高效率
  • 2)leader等待follower写日志成功并收到返回的acks后,才返回成功.这样leader宕机.重新选举的leader与宕机leader数据一致,数据不丢失.但因为要等待follower返回,效率较慢.一般采用少数服从多数的选举方式,如果要应对f个副本宕机,则至少需要2f+1个副本并使其中的f+1个写成功. kafka没有使用上述机制. 它实现了ISR(In-Sync Replication)的机制

十三.kafka的偏移量offset存放在哪儿.为什么?

  • 从kafka-0.9版本以后,kafka的消费者组和offset信息就不存zookeeper了,而是存到broker服务器上,所以,如果你为某个消费者指定了一个消费者组名称(group.id),那么,一旦这个消费者启动,这个消费者组名和它要消费的哪个topic的offset信息就会被记录在broker服务器上

1.概述

  • kafka版本[0.10.1.1],已默认将消费的offset迁入到了kafka一个名为__consumer_offsets的topic中,其实,早在0.8.2.2版本,已支持存入消费的offset到topic中,只是那时默认是将消费的offset存放在zookeeper集群中. 那现在,官方默认将消费的offset存储在kafka的topic中,同时,也保留了存储在zookeeper的接口,通过offsets.storage属性来设置

2.内容

  • 其实官方这样推荐,也是有其道理的. 之前版本,kafka其实存在一个比较大的隐患,就是利用zookeeper来存储记录每个消费者/组的消费进度. 虽然,在使用过程当中,JVM帮助我们完成了一些优化,但是消费者需要频繁的去与zookeeper进行交互,而利用zkClient的API操作zookeeper频繁的write其本身就是一个比较低效的action,对于后期水平扩展也是一个比较头疼的问题. 如果期间zookeeper集群发生变化,那kafka集群的吞吐量也跟着受影响. 在此之后,官方其实很早九提出了迁移到kafka的概念,只是,之前是一直默认存储在zookeeper集群中,需要手动设置,如果,对kafka的使用不是很熟悉的话,我们就接受了默认的存储. 在新版kafka及以后的把呢不能,kafka消费的offset都会默认存放在kafka集群中的一个叫__consumer_offsets的topic中

十四.如何保证kafka消息有序

  • kafka只能保证一个partition中的消息被某个consumer消费时是有序的,事实上,从topic角度来说,当有多个partition时,消息仍不是全局有序的

十五.kafka分区数

  • 分区数并不是越多越好,一般分区数不要超过集群机器数量,分区数越多占用内存越大(ISR等),一个节点集中的分区也就越多,当他宕机的时候,对系统影响也就越大
  • 分区数一般设置为:3-10个

十六.kafka分区分配策略

  • kafka内部存在两种默认的分区分配策略:Range 和RoundRobin

1.Range

  • 默认策略,Range是对每个topic而言的(即一个个topic的分),首先对同一个topic里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序. 然后用partitions分区的个数除以消费者线程的总数来决定每个消费者线程消费几个分区.若除不尽,那么之前几个消费者线程将会多消费一个分区
  • 例如:我们有10个分区,两个消费者(C1,C2),三个消费者线程,10/3=3而且除不尽
  • C1-0将消费0,1,2,3 分区
  • C2-0将消费4,5,6分区
  • C2-1将消费7,8,9分区

2.RoundRobin

  • 前提: 同一个consumer group 里面的所有消费者的num.streams(消费者消费线程数)必须相等,每个消费者订阅的主题必须相同
  • 将所有主题分区组成TopicAndPartition 列表,然后对TopicAndPartition列表按照hashCode进行排序,最后按照轮询的方式发送给每一个消费者线程

十七.kafka数据量计算

  • 每天总数据量100g,每天生产1亿条日志,10000万/24/60/60=1150条/秒
  • 平均每秒钟: 1150条
  • 低谷每秒钟: 400条
  • 高峰每秒钟: 1150条*(2-20倍)=2300-23000条
  • 每条日志大小: 0.5-2k
  • 每秒多少数据量: 2.3M-20M

十八.kafka消息数据积压,kafka消费能力不足怎么处理

  • 如果是kafka消费能力不足,则可以考虑增加topic的分区数,并且同时提升消费组的消费者数量,消费者数=分区数
  • 如果是下游的数据处理不及时,提高每批次拉取的数量,批次拉去数量过少(拉取数据/处理时间<生产速度) ,使处理的数据小于生产的数据,也会造成数据积压

十九.kafka高吞吐的实现

1.顺序读写

  • Kafka 的消息是不断追加到文件中的,这个特性使 Kafka 可以充分利用磁盘的顺序读写性能。顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,所以速度远快于随机读写。

2.零拷贝

  • 在 Linux kernel2.2 之后出现了一种叫做"零拷贝(zero-copy)"系统调用机制,就是跳过“用户缓冲区”的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到“用户态缓冲区”。零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数。通常是说在 IO 读写过程中。“零拷贝技术”只用将磁盘文件的数据复制到页面缓存中一次,然后将数据从页面缓存直接发送到网络中。

3.分区

  • Kafka 的队列 topic 被分为了多个区 partition,每个 partition 又分为多个段 segment,所以一个队列中的消息实际上是保存在 N 多个片段文件中通过分段的方式,每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力。

4.批量发送

  • Kafka 允许进行批量发送消息,先将消息缓存在内存中,然后一次请求批量发送出去比如可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去如 100条消息就发送,或者每 5 秒发送一次这种策略将大大减少服务端的 I/O 次数。

5.数据压缩

  • Kafka 还支持对消息集合进行压缩,Producer 可以通过 GZIP 或 Snappy 格式对消息集合进行压缩压缩的好处就是减少传输的数据量,减轻对网络传输的压力。

6.Consumer 的负载均衡

扫描二维码关注公众号,回复: 11674625 查看本文章
  • 当一个 group 中,有 consumer 加入或者离开时,会触发 partitions 均衡.均衡的最终目的,是提升topic 的并发消费能力。

猜你喜欢

转载自blog.csdn.net/sun_0128/article/details/108069129