kafka面试题

介绍一下消息队列

消息队列,是应用程序与应用程序的通信方法(其他如:rpc,http调用,webservice等)

主要解决应用解耦,异步消息,流量削锋等问题。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ

而部分数据库如Redis、Mysql以及phxsql也可实现消息队列的功能。

应用耦合:多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败;

异步处理:多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间;

限流削峰:广泛应用于秒杀或抢购活动中,避免流量过大导致应用系统挂掉的情况;把请求放到队列中一个一个进行处理,而不时直接进行大量处理,达到削峰的效果

 

为什么要解耦:

相互耦合的系统,一个系统出问题,也会影响到另一个系统,影响应用的稳定性。

如:订单系统和支付系统,支付系统调用订单系统的API;下单成功,然后系统异常,支付系统调用订单系统失败无法支付。

 

系统间解耦的方式:系统间的解耦就是把数据存储起来供其他业务调用

共享数据库

消息队列

 

redis实现消息队列:

通过list数据类型,进行加入数据,和拉取数据来实现;属于点对点的消息队列

通过发布订阅来实现。

 

mysql实现消息队列:

插入数据就是生产消息,获取并删除数据就是消费消息。属于点对点的消息队列

 

 

 

消息队列的模式

JMS规范目前支持两种消息模型:点对点和发布/订阅

ActiveMQ遵循了JMS规范,实现了点对点和发布订阅模型,但其他流行的消息队列RabbitMQ、Kafka并没有遵循JMS规范。

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件的API

Kafka它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现。

 

 

点对点模式

包括三个角色:消息队列、发送者 (生产者)、接收者(消费者)

发送者生产消息发送到queue中,消息接收者从queue中取出并且消费消息。

消息被消费以后,queue中不再有存储,所以消息接收者不可能消费到已经被消费的消息。

点对点模式特点:

队列支持多个消费者,但每个消息只能被一个接收者(Consumer)消费(即一旦被消费,消息就不再在消息队列中);

发送者和接收者间没有依赖性,发送者发送消息之后,不管有没有接收者在运行,都不会影响到发送者下次发送消息;

接收者在成功接收消息之后需向队列应答成功,以便消息队列删除当前接收的消息;消息不可以重复消费

 

发布订阅模式

包括三个角色:角色主题(Topic)、发布者(Publisher)、订阅者(Subscriber)

发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。所以这种模式的消息可以重复消费

发布/订阅模式特点:

每个消息可以有多个订阅者;

为了消费消息,订阅者需要提前订阅该角色主题,并保持在线运行;

 

介绍一下kafka

Kafka是Apache开发的一种高吞吐量的分布式发布订阅消息系统;

使用kafka可以处理应用解耦、异步处理、流量削峰等问题;

需要消费者订阅topic, 生产者发送消息到topic,消费者循环拉去消息进行消费;消费者是按组管理的,同一条消息只会被同一个组中的一个消费者消费,同一条消息可以被不同组的消费者共同消费。

kafka支持topic消息的分布式存储和建立分片副本实现高可用

kafka也有消息确认机制,默认生产的消息主分片保存成功返回确认消息,消费信息的确认就是消费端上报偏移量(kafka会记录消费者的offset信息到一个topic中)。

kafka消费端默认是自动提交偏移量的,我们一般设置为手动提交,避免没有消费完就提交了偏移量如果中间报错就不能再消费那条消息了

 

kafka概念+kafka作用

(kafka在其中都是扮演数据传递的功能,不做数据处理)

Kafka的特点

  • 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒
  • 可扩展性:kafka集群支持热扩展(不需要停机就可扩展服务器);
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失;
  • 高并发:支持数千个客户端同时读写;
  • 支持实时在线处理和离线处理:可以使用Storm这种实时流处理系统对消息进行实时进行处理,同时还可以使用Hadoop这种批处理系统进行离线处理;

kafka消息确认机制

生产者发送消息后,kafka收到或持久化后做消息确认:(通过在生产端设置参数 request.required.acks控制)

  • 不确认(0):生产者不等待确认消息
  • 主分片确认(1默认):主分片写入成功后发送
  • 主分片和isr确认(-1/all):主分片写入成功并同步数据给isr所有副本分片洗写入成功后发送

消费者消费信息的确认:

消费者提交偏移量,kafka收到了就确认了消息被消费成功了。

kafka高吞吐量/速度快/高并发的原因

页缓存技术:

kafka写数据时先写入 Page Cache也可以称为(OS Cache系统缓存) 接下来由操作系统自己决定什么时候把 OS Cache 里的数据真的刷入磁盘文件

顺序读写

kafka的消息数据在磁盘上是顺序写入的,这样避免了硬盘磁头的寻道时间;比随机写入数据的读写速度更快(顺序读写比随机读写速度快)

零拷贝

消息数据直接从磁盘读取到内存就发送给了网卡进行网络传输

支持分布式存储

可以多个服务器共同提供读写服务

支持批量发送和数据压缩

多条消息可以进行数据压缩并批量发送,减轻对网络传输的压力。(消费端需进行解压)

支持批量拉取数据进行消费

 

kafka零拷贝

零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数。

 

传统的io会经过四次的数据拷贝:

1、第一次:操作系统将磁盘文件数据读取到操作系统内核缓冲区;

2、第二次:应用程序将内核缓冲区的数据,读取到用户空间的应用程序缓冲区;

3、第三次:应用程序将应用程序缓冲区中的数据,读取到socket套接字缓冲区;

4、第四次:操作系统将套接字缓冲区的数据,读取到网卡缓冲区,由网卡进行网络传输。

 

kafka的处理流程是:

磁盘文件数据->操作系统内核缓冲区->网卡缓冲区

kafka因为不需要到磁盘中的数据做进一步处理,省去了对应用程序缓冲区和套接字缓存区的数据拷贝提升了读取效率

零拷贝依赖与硬件和操作系统的支持,目前的计算机都支持通过网卡直接访问系统内存;

 

kafka可扩展性

集群容易扩展,集群不需要停机,新的机器配置文件配置好指定集群zookeeper地址,就可以加入集群了;可以在其他机器上拷贝一个配置文件,修改一下brokerid就可以

 

kafka与jms的区别

jms:jms是Java消息服务(Java Message Service),是java平台提供的一套消息服务技术规范

jms有两种消息传输模型:

点对点模式:一个消息对应一个消费者,消息被消费者取出消费后消息清除

消息发布/订阅模式:一对多,消息生产后推送给多个消费者(即使订阅者不可用处于离线状态)

 

kafka:一对多,消息生产后保存到队列中由多个消费者拉取数据(kafka的消息是根据时间及大小进行删除不是根据消费进行删除,默认是7天,5M(任何一个条件达到就会被删除,在server.properties文件中配置默认值))

 

kafka与rabbitMQ

持久化:

都支持持久化

 

吞吐量:

kafka的吞吐量方便优于rabbitMQ

数据的消费:

rabbitMQ支持拉取的方式也支持推送的方式,kafka只支持拉取的方式。

 

安全性:

 

批量处理:

rabbitMQ不支持批量处理,kafka支持批量处理

 

高可用:

rabbitMQ采用镜像备份机制保障高可用,kafka使用分片副本选举机制等保障高可用

 

应用场景

rabbitMQ适用于消息安全性较高的场景

kafka适用于吞吐量要求较高及数据量大的场景

 

rabbitMQ的消息确认机制:

  • 生产者确认机制是当生产者发送消息后,需要等待RabbitMQ服务器的确认消息,以确保消息已经被成功地发送到RabbitMQ服务器。如果RabbitMQ服务器没有收到消息或者消息发送失败,生产者没有收到一个确认消息,可以进行重发或者其他处理。
  • 消费者确认机制是消费者接收到消息后,需要向RabbitMQ服务器发送确认消息,以告诉服务器已经成功地接收并处理了该消息。如果消费者没有发送确认消息,RabbitMQ服务器会认为该消息没有被正确地处理,从而会将该消息重新发送给其他消费者进行处理。

 

kafka的应用场景

(1)消息系统。Kafka作为一款优秀的消息系统,具有高吞吐量、内置的分区、备份冗余分布式等特点,为大规模消息处理提供了一种很好的解决方案。

 

(2)应用监控。利用Kafka采集应用程序和服务器健康相关的指标,如CPU占用率、IO、内存、连接数、TPS、QPS等,然后将指标信息进行处理,从而构建一个具有监控仪表盘、曲线图等可视化监控系统。例如,很多公司采用Kafka与ELK(ElasticSearch、Logstash和Kibana)整合构建应用服务监控系统。

 

(3)网站用户行为追踪。为了更好地了解用户行为、操作习惯,改善用户体验,进而对产品升级改进,将用户操作轨迹、内容等信息发送到Kafka集群上,通过Hadoop、Spark或Strom等进行数据分析处理,生成相应的统计报告,为推荐系统推荐对象建模提供数据源,进而为每个用户进行个性化推荐。

 

(4)流处理。需要将已收集的流数据提供给其他流式计算框架进行处理,用Kafka收集流数据是一个不错的选择,而且当前版本的Kafka提供了Kafka Streams支持对流数据的处理。

 

  • 持久性日志。Kafka可以为外部系统提供一种持久性日志的分布式系统。日志可以在多个节点间进行备份,Kafka为故障节点数据恢复提供了一种重新同步的机制。同时,Kafka很方便与HDFS和Flume进行整合,这样就方便将Kafka采集的数据持久化到其他外部系统。

 

Kafka支持单机吗

可以单机,自己做测试或者学习的时候可以搭建单机环境

 

Kafka的一概念

broker: kafka 集群中的每个kafka服务器都称为broker。

controller:kafka集群中的leader角色的broker

topic:专题,kafka 对消息保存是根据Topic归类,

partition:分区/分片,一个topic可以被划分成多个partition(区),多个partition可以分配到多个broker上,每个partition在存储层面是append log文件。任何发布到此partition的消息都会被直接追加到log文件

Replication:副本,每个Partition都可以配置至少1个Replication(当仅1个Replication时即仅该Partition本身)多个Replication分配到多个broker上。

segment: 多个大小相等的段组成了一个partition。

Producer:消息发送者

Consumer:消息接收者

consumer group:消费者组,每个消费者组可以有多个消费者,对订阅的topic中的消息一次只能有一个组的一个消费者消费

zookeeper:无论是kafka集群,还是producer和consumer 都依赖于zookeeper 来保证系统可用性,zookeeper为集群保存一些元数据信息。

offset:用于定位消息位置的序列号,每个消息都有一个称为offset的有序的序列号

base offset:partition第一条消息的偏移量

high watemark(HW):partition中最新一条已提交消息的位移

log end offset(LEO):partition中下一条待写入消息的offset,是待写入的

 

Kafka采用副本、选举机制等方式保障节点和数据的高可用;RabbitMQ使用镜像备份机制保证节点和数据的可靠。

broker的leader

Kafka集群中多个broker,有一个会被选举为leader称为controller

Controller负责管理整个集群中分区和副本的状态,比如partition的leader 副本故障,由controller 负责为该partition重新选举新的leader 副本;当检测到ISR列表发生变化,由controller通知集群中所有broker更新其信息;或者增加某个topic分区的时候也会由controller管理分区的重新分配工作

都是zk通知Controller,然后controller做出具体措施

 

 

说一下consumer group

kafka是以consumer group消费者组来订阅topic;

每个consumer group可以包含多个consumer。

发送到topic的消息只会被订阅该topic的每个group中的一个consumer消费。

一个topic的消息将会在订阅该topic的group中的consumer之间均衡分配;

一个topic可以对应多个group,消息会被发送到所有订阅该topic的group中。

 

partition与consumer group

同一个partition内的消息只能被同一个组中的一个consumer消费。

启动多个组,则会使同一个消息被消费多次

 

分片数量比消费者数量小:

就会有消费者空闲始终拿不到数据(根据均衡算法,组里面那个消费者拿那个分片中的数据是固定的)

 

分片数量比消费者数量大:

会存在消费者消费多个分片的数据,如果是增倍数,会均分

 

消费端均衡算法,算法如下:

1.A=(partition数量/同分组消费者总个数)

2.M=对上面所得到的A值小数点第一位向上取整

3.计算出该消费者拉取数据的patition合集:Ci = [P(M*i ),P((i + 1) * M -1)]

 

Kafka的Producer和Consumer

kafka有两类客户端,一类叫producer(消息生产者),一类叫做consumer(消息消费者),客户端和broker服务器之间采用tcp协议连接

Producer将消息发布到指定的topic中

consumer以consumer group为单位订阅 topic消费消息

kafka的Partition

将一个topic划分成多个partitions,这样既可以降低对单机磁盘容量的要求,又可以提高系统消息的读写速率。

 

每个partition对应在broker的一个目录以<topic_name>-<partition_id>命名如:page_visits-0, page_visits-1,该文件夹存储该 patition 的所有消息和索引文件

 

kafka的segment

partition对应一个目录,使用.log文件存储消息,这个.log文件被称为segment,segment的大小可以配置指定,达到指定大小就会新建一个,kafka也会相隔一定时间就创建一个segment不管原先的segment是否达到设定值(当然没有数据过来时也不会创建)。

 

segment好处:

提高写入和去取效率,使用segment而不是一个大的文件,数据文件以该段中最小的offset命名,可以方便的找到消息的位置

方便删除,删除已segment为单位进行删除

 

 

说一下kafka的Replication

增强了系统的可靠性。

每个Partition都可以配置至少1个Replication(当仅1个Replication时即仅该Partition本身)多个Replication分配到多个broker上。Replication的个数可以通过broker的配置文件进行配置(kafka的config目录下的server.properties文件)

这里Replication就是Partition本身或Partition的备份

 

Leader和follower说明

任何一个partition都有一个leader和多个follower(可以没有,Replication为1的时候)。

Leader和follower是partition的Replication角色

主分片:处理所有的读写操作,维护ISR列表:跟踪所有的follower状态信息,如果落后主分片太多会把副本分片踢出ISR

副本分片:只需要和leader保持信息同步即可,follower定时去主动同步leader的数据,不提供读写消息功能

 

AR,ISR,OSR说明

AR

分区中的所有副本统称为AR(Assigned Repllicas)

OSR

分区中与leader副本同步滞后过多的副本(Out-Sync Relipcas)

isr的全称是:In-Sync Replicas isr(同步副本列表) 是一个副本的列表

与leader副本保持一定程度同步的副本(包括Leader)组成ISR,当follower副本落后太多或者失效时,leader副本会吧它从ISR集合中剔除。

用于从中选举分片的leader

 

ISR集合中的副本才有资格被选举为leader,而在OSR集合中的副本则没有机会(这个原则可以通过修改对应的参数配置来改变)

 

 

副本被踢出ISR的条件,满足其中一个就会被踢出

条件1:根据副本和leader 的交互时间差,如果大于某个时间差 就认定这个副本不行了,就把此副本从isr 中剔除,此时间差根据

配置参数rerplica.lag.time.max.ms=10000 决定 单位ms

 

条件2:根据leader 和副本的信息条数差值决定是否从isr 中剔除此副本,此信息条数差值根据配置参数rerplica.lag.max.messages=4000 决定 单位条

 

isr 中的副本删除或者增加 都是通过一个周期调度来管理的,ISR在ZooKeeper中维护

 

副本分片跟不上主分片可能原因

1、新的副本(这是很常见的情况,每个新的副本加入都需要一段信息同步的追赶时期)

2、网络IO等原因,某些机器IO处理速度变慢所导致持续消费落后。

3、进程卡住(Kafka 是Java 写出来的,像Full GC,及高频次GC就会卡住进程)

 

Zookeeper在kafka的作用

Kafka有自带zookeeper,我们可以自己决定用它自带的还是使用我们已经有的zookeeper

 

1、broker的leader选举,broker的节点存活状态监控,topic注册,topic和broker关系维护

2、作为其分布式协调框架,将消息生产、消息存储、消息消费的过程结合在一起。

3、实现生产者与消费者的负载均衡。

 

早期版本的kafka用zk做集群元数据存储,消费者的消费状态(offset)订阅的topic等,group的管理。考虑到zk本身的一些因素以及整个架构较大概率存在单点问题,新的版本中减少了对zk的依赖,zk就主要负责broker的leader选举,检查broker的存活等

 

offset在0.1之前保存在zk中,0.1之后保存在一个topic(__consumer_offsets)中,由于zk写的性能不高,会影响消息消费的效率;

可以在没有Zookeeper的情况下使用Kafka吗?

kafka依赖zookeeper,不能没有zookeeper,单机也需要

kafka配置说明

kafka的配置分为broker节点配置,生产端配置和消费端配置

 

broker节点配置

broker的id

端口

分片数量(默认为1)

分片副本数量(默认为1)

每个segment文件大小(默认1G)

滚动生成segment文件的最大时间(默认7天)

segment文件保留的最长时间(默认7天)

主分片与副本分片最长时间差(超过就移出ISR)

主分片与副本分片最大条数差(超过就移出ISR)

消息数据刷新到磁盘的时间间隔(默认为空没有限制,由操作系统决定刷新到磁盘的频率(大约5秒一次))

消息数据刷新到磁盘的消息条数阀值(默认为空没有限制,由操作系统决定刷新到磁盘的频率)

日志文件目录

zk集群地址

 

生产端的配置

kafka集群地址:可以配置一个或多个机器地址,并非需要所有的broker地址,因为生产者会从给定的broker里寻找其它的broker。

生产者id(可以指定也可以系统生成)

生产者重试次数:消息写入失败重新写的尝试次数,默认是0,超过次数抛异常

重试的时间间隔:默认100ms

asks消息强制备份数量:发送即成功,主分片成功即成功,主分片和ISR中都成功才成功

生产端等待响应的超时时间:超时重试或异常

是否开启幂等性功能:默认为false

事务id:默认为null

 

 

消费端的配置

消费者组id(需要指定)

消费者id(可以指定也可以系统生成)

zookeeper集群地址

每次获取数据的最大尺寸(可以一次获取多条消息,这里是总大小,大于该值的消息不去消费)

是否自动提交(消费消息不是消费一次提交一次,而是先在本地记录,并定期提交offset信息,所以会存在消费多次之后再提交)

自动提交的时间间隔(默认5秒)消费者拉取数据后每隔多少秒提交一下偏移量

消费者会话过期时间:默认为3秒。也就是说,如果消费者在这段时间内没有发送心跳,那么broker将会认为会话过期而进行分区重平衡。

心跳频率:消费者多久发送一次心跳,默认1秒

拉取最大间隔时间:拉取消息时如果超过了设置的最大拉取时间,则会认为消费者消费消息失败,kafka会重新进行重新负载均衡,以便把消息分配给另一个消费组成员。默认5分钟。 

是否支持批量拉取

max.poll.records :一次拉取的最大条数

 

心跳机制

Kafka消费者会定期向它所属的Broker发送心跳信号,表明它仍然存活并正常消费。

如果Broker在一定时间内没有收到消费者的心跳信号,它会认为该消费者已经宕机或消费消息超时,此时会触发rebalance操作。Rebalance操作会重新分配消费分区给存活的消费者,确保消息能够继续被消费。

rebalance机制

rebalance(重新平衡)主要是为了消费者组下的消费者可以均衡的消费分区。当达到一定的触发条件时会重新分配消费者与分区之间的关系

rebalance触发的条件:

  • 消费者组成员数发生变更:比如某个消费者挂掉时,原本分配给它的分区会自动转交给其他活跃的消费者;而当挂掉的消费者重新启动时,又会根据当前的消费者数量和分区情况,重新分配一些分区给它。
  • 订阅的主题数发生变更
  • 订阅主题的分区数发生变更

后面两种我们可以避免频繁调整topic数量和分区数量即可。对于第一种我们主要避免成员被错误的认为已停止而被踢出消费者组,当完成rebalance之后,每个 消费者都会定期地向 群协调器(Coordinator) 发送心跳,表明它还存活着(心跳频率可以通过消费端heartbeat.interval.ms设置,默认3秒)。如果超过一定时间(可以通过Consumer 端的参数 session.timeout.ms进行配置,默认10s)群协调器没有收到心跳,群协调器就认为这个消费者死了,就会把这个消费者从组中移除触发rebalance。另外max.poll.interval.ms(限定了消费者两次拉取消息的最大时间间隔,默认5分钟)值也会影响rebalance,如果消费者处理消息的时间超过了设定的max.poll.interval.ms值,那么协调器将该消费者踢出消费者组,并重新进行Rebalance。

 

上面涉及的那三个参数都是针对消费者组单独设置的。

 

消费数据过慢会触发reblance吗:

会间接的触发,我们这种的手动提交偏移量的话,如果一直不提交偏移量,该消费者就没办法拉取后面的数据(因为kafka会根据你提交的偏移量决定从哪个偏移量开始拉取,如果直接拉取就重复消费了),这样两次拉取时间超过了max.poll.interval.ms值就会触发rebalance.

群协调器:

主要负责协调消费者组内的成员关系以及处理消费者的注册、注销和群组成员关系的变化,当消费者组需要与群组协调器交互时,它会向Kafka集群发送请求,集群会根据当前的负载和配置来选择一个合适的Broker作为群组协调器来处理该请求。如果当前的群组协调器出现故障或不可用,Kafka集群会重新选举一个新的群组协调器来继续处理消费者组的请求。可以说群组协调器在Kafka集群中是动态分布的,而不是固定在某一个节点上。

 

rebalance的影响:当触发Rebalance时,Kafka会暂停所有消费者的消费操作,以便重新分配分区给消费者。这个过程中,消费者组内的所有实例都不能消费任何消息

 

rebalance过程:

协调者会从现有的消费者实例中选择一个作为Rebalance的Leader。这个Leader将负责协调和控制整个Rebalance过程。

Leader会与其他消费者实例进行通信,收集它们当前的订阅信息和分区分配情况。

基于收集到的信息和Rebalance算法,Leader会计算出新的分区分配方案。

Leader将新的分区分配方案通知给消费者组内的所有消费者实例。每个消费者实例根据新的分配方案开始消费对应的分区。

消费者实例收到新的分区分配后,会更新自己的状态并开始消费新的分区。同时,它们也会提交新的偏移量,以确保在Rebalance过程中不会丢失消息或重复处理消息。

 

分区分配策略:

  1. Range分配策略:会将消费组内消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围。如果分区不能平均分配,那么字典序靠前的消费者会被多分配一个分区。这种策略简单高效,但当节点数量发生变化时,可能导致较大的分区迁移代价。
  2. Round Robin分配策略:按照节点的ID轮流分配分区。这种策略避免了Range分配策略中的分区迁移问题,但在某些情况下可能会导致某些节点负载过重。
  3. Sticky分配策略:以消费者ID为基础,尽量将同一个消费者消费的分区分配到同一个节点上。这种策略可以提高消费者的局部性,从而提高缓存利用率。但如果消费者数量变化较大时,也可能导致分区迁移的代价比较大。
  4. 自定义分配策略:用户可以根据自己的需求,通过实现org.apache.kafka.clients.consumer.ConsumerPartitionAssignor接口来定义自己的分区分配策略。这种策略可以最大程度地满足用户的特定需求,但也要考虑迁移成本和复杂度。

kafka默认使用range策略+sticky策略,会对每个主题中的分区按照序号进行排序,并对消费者按照字母顺序进行排序,然后通过计算分区数除以消费者数来决定每个消费者应该消费几个分区。而Sticky策略则会在尽量保持原有分区分配的基础上,进行消费者组内的分区再分配。

 

Rebalance只针对通过subscribe方式不指定分区消费的情况,如果通过assign方式指定了分区,Kafka则不会进行Rebalance。

 

Partition和Replication数量选择

Partition数量:

理论上partition数量越多整个集群所能达到的吞吐量就越大。但partition会消耗系统资源如内存,文件句柄。

如何来确定多少合适,可以通过实际的测试来确定,满足需求就可以

 

我们的项目:

我们是3台机器配置了3个

 

kafka的元数据

每台broker在内存中都维护了集群上所有节点和topic分区的状态信息:

集群的controller的brokerId信息

集群所有broker的一些元数据信息,比如id,

集群所有分区的信息如分区的leader,ISR等

 

更新元数据信息由Controller控制,Controller更新完会发给其他broker更新请求,让他们更新

只要集群中有broker或分区数据发生了变更就会更新这些cache

Controller是通过zookeeper来感知这些数据的变化的

 

kafka对文件系统缓存的使用

kafka的读写都是先去文件系统操作

先写入文件系统缓存,然后刷到磁盘上(默认根据操作系统自己定时刷每5秒刷一次,也可以直接配置刷的策略);

读消息的时候也是先在文件系统缓存中读,读到了就返回,未读到就从磁盘获取,放入文件系统缓存中并返回。

 

即使Kafka重启了,Page Cache还依然在。但机器重启了,没有刷新到磁盘的数据就丢失了

 

kafka如何保证消息的顺序

Kafka 保证一个 Partition 内的消息的有序性,消息保存到分片中是顺序保存的,消费也是按照文件中消息的顺序来进行消费。

所以单个消费者去消费一个partition中的数据是有序的,可以要保持顺序性的消息设置相同的key就会到同一个partition,可以被顺序消费。

 

消费端要提高吞吐量可以批量获取消息,放到内存中,相同key的放到一个内存队列中对应一个线程处理。

 

只要保证一个partition中的数据能按顺序处理就行

 

Procucer生产消息

生产端有配置broker-list,根据这个列表,生产端请求一个broker,broker获取到集群中指定topic的分片元数据,返回给生产端,生产端根据路由机制选择其中一个分片leader发出写入请求,leader把数据写入成功并通知ISR中的副本来拉取数据同步,副本同步完数据给leader发送ACK(确认字符),leader收到所有的ACK后增加 HW(high watermark,最后 commit 的 offset) 并向 producer 发送 ACK告诉消息写入完毕可以进行消费。

 

写数据的成功:

向 Kafka 发送数据并不是真要等数据被写入磁盘才会认为成功,而是只要数据被写入到操作系统的页缓存(Page Cache)上就代表写入成功,就会返回成功。操作系统默认5秒刷到磁盘

 

 

其路由机制为:

  1. 生产者指定了 patition,则直接使用;
  2. 未指定 patition 但指定 消息的key,通过对 key 的 值进行hash 对patition数量取模选出一个 patition
  3. patition 和 key 都未指定,使用轮询选出一个 patition。

 

写入流程

  1. producer 先从 zookeeper 的 "/brokers/.../state" 节点找到该 partition 的 leader
  2. producer 将消息发送给该 leader
  3. leader 将消息写入文件系统内存
  4. followers 从 leader pull 消息,写入文件系统内存 后 leader 发送 ACK(确认字符)
  5. leader 收到所有 ISR 中的 replica 的 ACK 后,增加 HW(high watermark,最后 commit 的 offset) 并向 producer 发送 ACK

 

文件系统内存中的内容会由操作系统定时刷到磁盘,也可以配置自己的策略什么时候进行刷磁盘。

 

批量写数据:

batch.size

通过这个参数来设置批量提交的数据大小,默认是16k,当积压的消息达到这个值的时候就会统一发送(发往同一分区的消息)

linger.ms

这个设置是为发送设置一定是延迟来收集更多的消息,默认大小是0ms(就是有消息就立即发送)

 

当这两个参数同时设置的时候,只要两个条件中满足一个就会发送。比如说batch.size设置16kb,linger.ms设置50ms,那么当消息积压达到16kb就会发送,如果没有到达16kb,那么在第一个消息到来之后的50ms之后消息将会发送。

consumer如何消费消息

consumer 采用 pull (拉)模式从 broker 中读取数据。

push 模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而 pull 模式则可以根据 consumer 的消费能力以适当的速率消费消息。

 

拉取采用while(true)循环的方式去循环拉取数据

kafka消息的持久化存储

kafka写数据是先写入文件系统内存中,然后刷新到磁盘(默认根据操作系统定时刷新到磁盘(默认5s),也可以进行kafka配置进行指定时间间隔或消息条数来刷到磁盘上(默认是没有设置的))

kafka写入数据到系统缓存中后,没有持久化之前是可以被消费的。

如果持久化之前服务器宕机可能导致数据丢失。

 

log文件

kafka收到消息会持久化到磁盘上,不考虑副本的情况下,每个分片/分区对应一个log文件夹log文件夹下有多个segment文件(以.log为后缀的文件)及segment相关的其他文件

segment文件

记录了消息的内容,每个segment文件还对应两个索引文件(以.index和.timeindex为后缀的文件),分别是偏移量索引文件(.index)和时间戳索引文件(.timeindex)

还有其他的与segment对应的文件如.deleted文件等

索引文件

偏移量索引文件:

记录了偏移量和对应消息记录在日志分段文件中的物理位置的映射关系。用于提高读取、写入性能。

时间戳索引文件:

则记录了时间戳和对应消息记录在日志分段文件中的物理位置的映射关系。用于提高查找消息的效率。可以用于定位消息,日志的清除和日志切分,时间戳是对应消息生成的时间

 

定位

清除:可以根据其中的时间戳来判断是否要清除

切分:可以根据日志的最大时间戳,如发生于7天前的话,就创建一个新的日志段文件

 

 

kafka消息(日志)删除和压缩

消息是删除还是压缩使用log.cleanup.policy参数来控制,delete(默认值)就是删除,compact就是压缩,也可以同时启动,设置为delete,compact

 

消息的删除策略:

策略有3种:

基于时间的删除策略、基于日志大小的删除策略、基于日志起始偏移量的删除策略。

 

如何进行删除:

执行日志分段的删除任务时,会首先从Log对象中维护的日志分段的跳跃表中移除需要删除的日志分段,然后将日志分段所对应的文件和索引文件添加.deleted后缀。最后转交给名称为delete-file任务来删除以.deleted为后缀的文件,执行延迟时间可通过参数file.delete.delay.ms控制,默认为1分钟。

 

基于时间:默认时间策略为7天。

根据segment对应的最大时间戳或最近修改时间判断

获取最大时间戳需要查询该日志分段所对应的时间戳索引文件(.timeindex文件)中最后一条索引项,若该索引项的时间戳大于0就取该值计算,否则就取最近修改时间,因为最近修改时间常常会被更新所以先根据最大时间戳。

 

基于日志大小:默认为不采取这种策略

该策略会依次检查分片日志的总大小是否超过设置的阀值,超过就会对最旧的segment文件执行删除策略,删除多少个要看超过了阀值多少,删除到小于阀值就可以了

 

基于日志起始偏移量:

查询分片的每个segment文件的起始偏移量,如果小于指定的偏移量就会进行删除

日志压缩:

针对每个消息的key进行整合,对于有相同key的的不同value值,只保留最后一个版本。

没有详细了解

删除是以segment为单位进行删除的

 

未被消费的消息会被删除吗:

kafka的消息删除跟是否消费没有关系,如果达到了kafka设置的时间或大小限制就会删除

 

 

kafka 消息传送机制/重复发送

对于JMS实现,消息传输担保非常直接:有且只有一次(exactly once).在kafka中稍有不同:

  1. at most once: 消息不会被重复发送,但是可能丢失
  2. at least once: 消息可能会被重复发送,但是不会漏发送,消息至少发送一次,如果消息未能接受成功,可能会重发,直到接收成功.
  3. 3) exactly once:不会少发送也不会重复发送,只会发送一次 .

    

第三种定义是大家所希望的消息队列的保证。

当acks设置的0,生产者发送消息不需要确认是否成功保存,就表示at most once

当acks设置的1或者-1,ack超时或者返回错误,生产者就重新发送消息,就有可能消息重复保存

 

0.11版本kafka引入了幂等性,无论producer向kafka发送多少相同的消息,kafka只会持久化一条;也就是也就是说at-least-once + 幂等性 = at-exactly-once。

 

幂等性说明

启用幂等性:生产端可以设置配置参数来开启幂等性,默认是关闭的;

启用幂等性的生产者在创建的时候会自动分配一个PID(Producer ID 生产者id),并且发送给同一个分区的消息会携带序列号SeqNum;

broker端缓存<PID, PartitionKey, SeqNum>,当有相同key的消息时,broker只会持久化一条

启用幂等性后,kafka自动将acks属性设为-1。

 

消息丢失和重复消息

一旦消息被提交成功,那么只要有任何一个保存了消息的副本存活,这条消息被视为不会丢失的。

 

生产者丢失消息情况

asks设置的0,不需要确认是否写入成功,如果没有写入成功,消息丢失

asks设置的1,只需要leader写入数据成功便认为成功,leader写入到文件系统内存后返回成功,消息还没有持久化到磁盘,leader所在的机器宕机,消息丢失;(基本可以保证兼具较好的吞吐和较高的可靠性,毕竟宕机几率小)

asks设置为-1,leader和ISR都确认写入成功便认为成功,除非一些不可抗力因素,

这种方式基本可以确保数据的完全不丢失。(如果ISR只有一个leader那跟配置1一样)

 

 

处理方法:

  • 消息发送失败可以进行重新发送
  • 发送消息之后回调函数,发送成功就发送下一条,发送失败就记在日志里,等着定时脚本(定时器)来扫描(发送失败可能并不真的发送失败,只是没收到反馈,定时脚本可能会重发)

 

 

消费者丢失消息情况

auto.commit.enable=true,消费端自动提交,当消费者拉到消息之后,还没有处理完 commit interval 提交间隔就到了,提交了offersets。这时consummer又挂了,重启后,从下一个offersets开始消费,之前的消息丢失了。设置auto.commit.enable=false,每次处理完手动提交。确保消息真的被消费并处理完成。

 

数据重复情况

acks设置的1或者-1时:

leader在返回ack之前就挂掉了,会从ISR中的follower中选出leader,如果生产端设置了重试,生产者没有收到leader的ack回应会重试投递,此时如果副本已经写入数据成功再重试就会造成数据重复。(默认设置是不重试的)

 

生产者重试机制

producer端可以配置retries(spring.kafka.producer.retries)表示最大重试次数,消息发送失败的情况下,重试发送的次数,默认是0次,超过次数后会抛出异常,用户可以选择停止发送后续数据也可选择继续选择发送。看具体业务端的逻辑实现

还可以配置重试的时间间隔(spring.kafka.producer.retry-backoff-ms),默认是100ms,指定收到发送失败结果后多长时间进行重新发送

spring.kafka.producer.retries该配置是spring提供的重试功能,不是Kafka自身提供的重试机制。

当消息发送失败并触发重试时,幂等性生产者会继续使用相同的PID和之前生成的序列号(sequence number)来重新发送消息。Kafka broker会根据PID和序列号来确保消息的唯一性,并避免重复处理相同的消息。

 

重复消费问题

出现重复的原因

  • 消息重复发送导致消息重复
  • 消费者消费后由于某些原因没有提交offset

重新消费

如果想要重新消费消费过的数据

  1. 修改offset

我们在使用consumer消费的时候,每个topic会产生一个偏移量,这个偏移量保证我们消费的消息顺序且不重复。我们修改这个offest到我们想重新消费的位置,就可以做到重新消费了

 

2、通过使用不同的group来消费

通过不同的group来重新消费数据方法简单,但我们无法指定我们要重复消费哪些数据,它会从这个groupid在zookeeper注册之后所产生的数据开始消费。这里需要注意的是新的group是重新消费所有数据,但也并非是topic中所有数据,它只会消费它在zookeeper注册过之后产生的数据。我们可以再zookeeper客户端中  /consumer/  目录下查看我们已经注册过的groupid。我们在使用consumer消费数据时如果指定一个新的groupid,那么当这个consumer被执行的时候会自动注册到zookeeper中。而这个group中的consumer之后消费到注册之后产生的数据。

 

消息的消费状态

Kafka不保存消息的状态,即消息是否被“消费”。kafka保存Consumer在Topic分区中的位置offset,在offset之前的消息是已被“消费”的,在offset之后则为未“消费”的。

消费端可以设置自动提交时间,默认是开启自动提交的,自动提交默认时间时5s,代表拉取时间后5秒进行提交offset

 

offset在0.1之前保存在zk中,0.1之后保存在一个topic中,由于zk写的性能不高,会影响消息消费的效率;

 

 

失败重试

Producer通过设定参数retries,如果发送消息到broker时抛出异常,且是允许重试的异常,那么就会最大重试retries参数指定的次数。

 

Consumer不能设置失败重试,如果不想自己实现消息重试机制,建议使用RocketMQ作为消息队列,RocketMQ的消息重试机制相当完善

自己实现消息重试:

消费端捕获失败的消息进行存储(记录日志或其他),手动或定时任务进行触发

也可以消费端把消费失败的消息发布到专门处理失败的topic进行重新消费

kafka集群搭建

kafka实例broker监听默认端口9092,kafka-manager默认情况下端口为9000, zookeeper 服务端启用的是默认端口 2181

先要搭建zookeeper集群:

选奇数台的服务器上面安装上zookeeper,复制zookeeper安装目录的conf/zoo_sample.cfg文件命名为zoo.cfg文件,然后在这个文件中设置zookeeper集群的各个ip,心跳时间,日志及数据保存地址等

调用./zkServer.sh start 启动zk服务

./zkServer.sh status 查看zk服务器状态(显示是否可用是主还是从)

jps命令查看是否启动 有QuorumPeerMain表示启动中

 

kafka集群搭建:

在多台服务器中安装kafka,修改安装目录下config目录下的server.properties文件修改broker.id,对外提供的端口号port默认是9092,host.name改为本机的ip,zookeeper集群的ip zookeeper.connect,消息保存最大值,消息保存的位置等信息

然后调用bin目录下的kafka-server-start.sh 指定配置文件server.properties就可以启动了

 

如果使用kafkamanager

下载解压kafka-manager,修改conf目录中打开 application.conf文件中的kafka-manager.zkhosts="localhost:2181" 也就是zookeeper集群地址

然后执行bin/kafka-manager 就可以启动(需要配置过java环境变量,否则:bin/kafka-manager -java-home /usr/local/oracle-java-8)

默认端口是9000,访问ip:端口就可以了

 

Kafka集群的扩容

扩容broker数量,分为扩容和分区重新分配两部分

扩容:机器配置一样的前提下只需要把配置文件里的brokerid改一个新的启动起来就可以。

重新分配分区:在原来机器上的主题分区不会自动均衡到新的机器,需要使用分区重新分配工具来均衡均衡,使用 bin/kafka-reassign-partitions.sh重新分配工具来完成

 

 

 

Kafka-manager

使用Kafka-manager可以对kafka多个集群进行管理,是雅虎开源的web工具

具体支持以下内容:

集群管理

集群状态监控(包括topics, consumers, offsets, brokers, replica distribution, partition distribution)

Topic管理

分区管理

 

简单说下整个系统运行的顺序:

(1)启动zookeeper 的 server

(2)启动kafka 的 server

(3)Producer 如果生产了数据,会先通过 zookeeper 找到 broker,然后将数据存放到 broker

  • Consumer 如果要消费数据,会先通过 zookeeper 找对应的 broker,然后消费。

 

kafka的topic创建partition指定等

可以使用命令行进行创建指定,也可以通过管理工具kafka Manager创建指定

broker的数量

看业务量,zookeeper需要奇数台来保证一致性,kafka的选举不采用少数服从多数也没必要奇数台

 

kafka读写分离

kafka不支持读写分离,读和写都是由主分片来完成,副本分片只为了高可用

可能是出于吞吐量考虑,避免

(es是主分片提供读写,副本分片提供读)

kafka的leader选举机制是什么

Broker的leader选举

Kafka的Leader选举是通过在zookeeper上创建/controller临时节点来实现leader选举,并在该节点中写入当前broker的信息

{“version”:1,”brokerid”:1,”timestamp”:”1512018424988”}

利用Zookeeper的强一致性特性,一个节点只能被一个客户端创建成功,创建成功的broker即为leader,即先到先得原则,leader也就是集群中的controller,负责集群状态维护。

当leader和zookeeper失去连接时,临时节点会删除,而其他broker会监听该节点的变化,当节点删除时,其他broker会收到事件通知,重新进行临时节点的创建也就是发起leader选举。

 

分片leader的选举

ISR集合方法

分区leader的选举是由broker的leader也就是Controller来进行的

选举策略:

根据kafka服务器的unclean(unclean.leader.election.enable)配置,如果未true表示不在ISR中的副本也有资格成为leader,如果为false表示没有资格。新版本默认都是false

为false:

按照 AR (全副本集)集合中副本的顺序查找第一个存活的副本,并且这个副本在 ISR 集合中且所在的broker是可以访问的(未宕机的),它就选为leader。

为true:

按照 AR (全副本集)集合中副本的顺序查找第一个存活的副本,如果这个副本所在的broker是可以访问的(未宕机的),它就选为leader。

 

说明:

AR中副本的顺序是在副本创建的时候顺序就固定了,不会变

如果不在ISR集合的副本选为了leader:

这个副本去同步其他副本可能会发现自己的内容还没有其他副本的内容新,其他副本就会把多出的内容删除掉,就发生了数据丢失。

所以如果系统的可用性高于可靠性也可以设置unclean参数为true;

 

 

 

为什么不用少数服从多数的方法

少数服从多数是一种比较常见的一致性算法和Leader选举法。它的含义是只有超过半数的副本同步了,系统才会认为数据已同步;选择Leader时也是从超过半数的同步的副本中选择。这种算法需要较高的冗余度。譬如只允许一台机器失败,需要有三个副本;而如果只容忍两台机器失败,则需要五个副本。而kafka的ISR集合方法,分别只需要两个和三个副本。

(zk的选举使用的少数服从多数的方式)

(es的选举也是要少数服从多数,选择nodeid最小的那个)

如果所有的ISR副本都失败了怎么办

  此时有两种方法可选,一种是等待ISR集合中的副本复活,一种是选择任何一个立即可用的副本,而这个副本不一定是在ISR集合中。这两种方法各有利弊,实际生产中按需选择。

  如果要等待ISR副本复活,虽然可以保证一致性,但可能需要很长时间。而如果选择立即可用的副本,则很可能该副本并不一致。

unclean.leader.election.enable 参数说明:

为false表示非ISR中的副本不能够参与选举

为true表示非ISR中的副本可以参与选举

Kafka 0.11.0.0版本开始默认值为false,以前是true

Kafka通信协议

Kafka的Producer、Broker和Consumer之间采用的是一套自行设计的基于TCP层的协议。Kafka的这套协议完全是为了Kafka自身的业务需求而定制的

 

kafka的事务

kafka从0.11版本开始支持消息事务特性

消息的事务特性:一系列的生产、消费操作要么都完成,要么都失败

消息事务是实现分布式事务的一种方案,可以确保分布式场景下的数据最终一致性。

Kafka的幂等性可以保证生产只对一个分区实现Exactl once语义(即不会少发又不对重复),需要多个分区也实现这个语义,还需要引入消息事务确保原子性。

 

实现方法

是有一个事务的协调者,刚开始生产端告诉协调者要进行事务,协调者给他返回一个produce ID,生产端开启事务开始生产消息,消息被保存(在每个分片上保存都是幂等的)但消费端还看不到,生产端成功提交事务或回滚事务(如果是回滚消息被标记对消费端不可见),事务终止。事务终止如果为成功消费端可以消费消息。

 

场景

Kafka 的事务处理,主要是允许应用可以把消费和生产的批量处理(涉及多个 Partition)在一个原子单元内完成,操作要么全部完成、要么全部失败。

 

使用:

broker,生产端,消费端都有事务相关的配置

broker设置事务超时时间,事务状态topic及副本数量等

生产端要指明幂等性设置为开启,还要设置transactional.id(用于事务性交付)

消费端设置以偏移量顺序使用非事务性消息或已提交事务性消息。

要在生产端调用开启事务,提交事务等的api

 

spring-integration-kafka2.0.0使用

导入整合的这个包,编写消费端的配置文件,指明groupid,topic,kafka集群地址,指定该topic

要消费的处理类,消费处理类implements MessageListener<Integer, String>

在实现的方法 onMessage方法中处理消息就可以了

 

logback结合kafka发消息

导入logback-kafka-appender包,然后在logback的配置文件中appender标签中指定KafkaAppender,指定kafka集群ip地址,指定topic,消息发送延时,批处理大小,处理的日志级别及日志产生的包或类等信息就可以了

 

kafka常用命令

启动zookeeper

bin/zookeeper-server-start.sh config/zookeeper.properties &

启动kafka

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

停止kafka

bin/kafka-server-stop.sh

停止zookeeper

bin/zookeeper-server-stop.sh

创建topic

bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test

展示topic

bin/kafka-topics.sh --list --zookeeper localhost:2181

描述topic

bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic

 

生产者:

bin/kafka-console-producer.sh --broker-list 130.51.23.95:9092 --topic my-replicated-topic

消费者:

bin/kafka-console-consumer.sh --zookeeper 130.51.23.95:2181 --topic test --from-beginnin

查看条数

./kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list localhost:9092 --topic my-topic --time -1

删除话题

/kafka-topics.sh --zookeeper localhost:2181 --delete --topic my-topic

 

spring中怎么消费topic消息

使用@KafkaListener注解来配置消费的topic信息

@Service  
public class KafkaConsumerService {  
    @KafkaListener(topics = "my-topic", groupId = "my-group")  
    public void consume(ConsumerRecord<?, ?> record) {  
        System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());  
    }  
}

 

项目相关

在公司使用kafka进行记录用户动作消息,统计计算服务订阅这些消息,根据消息来做统计工作

 

通过kafka manager管理的

3个zookeeper服务器,3台kafka服务器,5个topic,每个topic分3个partition,每个partition分2个Replication

每个topic建了一个group

首先搭建了zookeeper集群,然后kafka集群,编写了消费端,在应用端添加消息的发送功能(用户的行为动作(点赞,评论,关注),挑战及练习产生的数据信息等),处理的是统计信息,我通过消费端处理消息把每个人的统计信息写入到redis和数据库,另一个人读取redis中的数据加工成排行榜及其他应用端要用到的统计数据写入到数据库然后编写的dubbo服务对外提供服务

 

kyxl是利用logback和kafka的整合包,在logback的配置文件中配置kafkaAppender,指定kafka集群

 

posted @ 2023-02-01 15:59  星光闪闪  阅读(145)  评论(0)    收藏  举报