2、kafka知识点

1,148 阅读13分钟

kafka三种能力

1、数据的发布和订阅能力(消息队列)

2、数据的分布式存储能力(存储系统)

3、数据的实时处理能力(流处理引擎)

kafka消息模型

image.png

Broker(代理): Kafka集群中,一个kafka实例被称为一个代理(Broker)节点。

Producer(生产者): 消息的生产者被称为Producer。
Producer将消息发送到集群指定的主题中存储,同时也自定义算法决定将消息记录发送到哪个分区。

Consumer(消费者):
消息的消费者,从kafka集群中指定的主题读取消息。

Topic(主题):
主题,kafka通过不同的主题区分不同的业务类型的消息记录。

Partition(分区):
每一个Topic可以有一个或者多个分区(Partition)。

分区和代理节点的关系:
一个分区只对应一个Broker,一个Broker可以管理多个分区。

副本(Replication):
每个主题在创建时会要求制定它的副本数(默认1)。

记录(Record):
实际写入到kafka集群并且可以被消费者读取的数据。
每条记录包含一个键、值和时间戳。

kafka将所有消息进行了持久化存储,由消费者自己各取所需,想取哪个消息,想什么时候取都行,只需要传递一个消息的offset即可。

零拷贝

零拷贝(Zero-copy)技术指在计算机执行操作时,CPU不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及CPU的拷贝时间。它的作用是在数据报从网络设备到用户程序空间传递的过程中,减少数据拷贝次数,减少系统调用,实现CPU的零参与,彻底消除CPU在这方面的负载。
实现零拷贝用到的最主要技术是DMA数据传输技术和内存区域映射技术。

传统IO过程

image.png

上下文切换:当用户程序向内核发起系统调用时,CPU将用户进程从用户态切换到内核态;当系统调用返回时,CPU 将用户进程从内核态切换回用户态。
CPU拷贝:由CPU直接处理数据的传送,数据拷贝时会一直占用CPU的资源。
DMA拷贝:由CPU向DMA磁盘控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给 CPU,从而减轻了CPU资源的占有率。

(1)操作系统将数据从磁盘文件中读取到内核空间的页面缓存;
(2)应用程序将数据从内核空间读入用户空间缓冲区;
(3)应用程序将读到数据写回内核空间并放入socket缓冲区;
(4)操作系统将数据从socket缓冲区复制到网卡接口,此时数据才能通过网络发送。

mmap+write

一种零拷贝方式是使用mmap+write代替原来的read+write方式,减少了1次CPU拷贝操作。mmap Linux提供的一种内存映射文件方法,即将一个进程的地址空间中的一段虚拟地址映射到磁盘文件地址。

使用mmap的目的是将内核中读缓冲区(read buffer)的地址与用户空间的缓冲区(user buffer)进行映射,从而实现内核缓冲区与应用程序内存的共享,省去了将数据从内核读缓冲区(read buffer)拷贝到用户缓冲区(user buffer)的过程,然而内核读缓冲区(read buffer)仍需将数据拷贝到内核写缓冲区(socket buffer),大致的流程如下图所示: image.png

基于mmap + write系统调用的零拷贝方式,整个拷贝过程会发生4次上下文切换,1次CPU拷贝和2次DMA拷贝,用户程序读写数据的流程如下:

1、用户进程通过mmap() 函数向内核(kernel)发起系统调用,上下文从用户态(user space)切换为内核态(kernel space)。
2、将用户进程的内核空间的读缓冲区(read buffer)与用户空间的缓存区(user buffer)进行内存地址映射。
3、CPU利用DMA控制器将数据从主存或硬盘拷贝到内核空间(kernel space)的读缓冲区(read buffer)。 上下文从内核态(kernel space)切换回用户态(user space),mmap系统调用执行返回。
4、用户进程通过 write() 函数向内核(kernel)发起系统调用,上下文从用户态(user space)切换为内核态(kernel space)。
5、CPU将读缓冲区(read buffer)中的数据拷贝到的网络缓冲区(socket buffer)。
6、CPU利用DMA控制器将数据从网络缓冲区(socket buffer)拷贝到网卡进行数据传输。
7、上下文从内核态(kernel space)切换回用户态(user space),write 系统调用执行返回。

mmap主要的用处是提高I/O性能,特别是针对大文件。对于小文件,内存映射文件反而会导致碎片空间的浪费,因为内存映射总是要对齐页边界,最小单位是4KB,一个5KB的文件将会映射占用 8KB内存,也就会浪费3KB内存。

sendfile

通过sendfile系统调用,数据可以直接在内核空间内部进行I/O传输,从而省去了数据在用户空间和内核空间之间的来回拷贝。与mmap内存映射方式不同的是,sendfile调用中I/O数据对用户空间是完全不可见的。也就是说,这是一次完全意义上的数据传输过程。

image.png

基于sendfile系统调用的零拷贝方式,整个拷贝过程会发生2次上下文切换,1 次CPU拷贝和2次DMA拷贝,用户程序读写数据的流程如下:

1、用户进程通过sendfile()函数向内核(kernel)发起系统调用,上下文从用户态(user space)切换为内核态(kernel space)。
2、CPU利用DMA控制器将数据从主存或硬盘拷贝到内核空间(kernel space)的读缓冲区(read buffer)。
3、CPU将读缓冲区(read buffer)中的数据拷贝到的网络缓冲区(socket buffer)。
4、CPU利用DMA控制器将数据从网络缓冲区(socket buffer)拷贝到网卡进行数据传输。
5、上下文从内核态(kernel space)切换回用户态(user space),sendfile系统调用执行返回。

相比较于mmap内存映射的方式,sendfile少了2次上下文切换,但是仍然有1次CPU拷贝操作。sendfile存在的问题是用户程序不能对数据进行修改,而只是单纯地完成了一次数据传输过程。

RocketMQ选择了mmap + write这种零拷贝方式,适用于业务级消息这种小块文件的数据持久化和传输;而Kafka采用的是sendfile这种零拷贝方式,适用于系统日志消息这种高吞吐量的大块文件的数据持久化和传输。但是值得注意的一点是,Kafka的索引文件使用的是mmap+write方式,数据文件使用的是sendfile方式。

Kafka中的领导者副本(Leader Replica)和追随者副本 (Follower Replica)的区别

副本当前分为领导者副本和追随者副本。只有Leader副本才能对外提供读写服务,响应Clients端的请求。Follower副本只是采用拉(PULL)的方式,被动地同步Leader副本中的数据,并且在Leader副本所在的Broker宕机后,随时准备竞选Leader副本。

kafka为什么那么快?

1、顺序写磁盘,在顺序读写的情况下,磁盘的顺序读写速度和内存持平。
2、使用零拷贝技术,减少了用户态和内核态的切换,降低对文件的拷贝次数,一定程度上提升了速度。
3、利用Partition实现并行处理。一方面,由于不同Partition可位于不同机器,因此可以充分利用集群优势,实现机器间的并行处理。另一方面,由于Partition在物理上对应一个文件夹,即使多个Partition位于同一个节点,也可通过配置让同一节点上的不同Partition置于不同的磁盘上,从而实现磁盘间的并行处理,充分发挥多磁盘的优势。
4、利用了分页存储Page Cache来利用内存提高I/O效率。
页缓存是操作系统对数据文件的读写提供的一种缓冲技术,目的是为了减少I/O操作的次数。 当进程读取某个文件的时候,操作系统会先查看待读取的数据所在的页是否在页缓存中,如果在则直接返回给进程,如果不在则从磁盘读取后先写入页缓存,然后再将数据返回给进程。 当进程写入某个文件的时候,操作系统也会查看需要操作的页是否在页缓存中,如果存在则进行写入,如果不在则从磁盘中读取后再进行相关写入操作,被修改后的页,操作系统会在合适的时间内刷入磁盘来保证数据的一致性。
5、批处理。Kafka的客户端和broker还会在通过网络发送数据之前,在一个批处理中累积多条记录 (包括读和写)。
6、数据压缩。Producer可将数据压缩后发送给broker,从而减少网络传输代价,目前支持的压缩算法有:Snappy、Gzip、LZ4。数据压缩一般都是和批处理配套使用来作为优化手段的。

zhuanlan.zhihu.com/p/183808742

kafka中consumer group是什么概念

同样是逻辑上的概念,是Kafka实现单播和广播两种消息模型的手段。同一个topic的数据,会广播给不同的group;同一个group中的worker,只有一个worker能拿到这个数据。换句话说,对于同一个topic,每个group都可以拿到同样的所有数据,但是数据进入group后只能被其中的一个worker消费。group内的worker可以使用多线程或多进程来实现,也可以将进程分散在多台机器上,worker的数量通常不超过partition的数量,且二者最好保持整数倍关系,因为Kafka在设计时假定了一个partition只能被一个worker消费(同一group内)。

Kafka中的消息是否会丢失和重复消费?

要确定Kafka的消息是否丢失或重复,从两个方面分析入手:消息发送和消息消费。

1、消息发送

Kafka消息发送有两种方式:同步(sync)和异步(async),默认是同步方式,可通过producer.type属性进行配置。Kafka通过配置request.required.acks属性来确认消息的生产:

0---表示不进行消息接收是否成功的确认; 1---表示当Leader接收成功时确认; -1(all)---表示Leader和Follower都接收成功时确认;
综上所述,有6种消息生产的情况,下面分情况来分析消息丢失的场景:

(1)acks=0,不和Kafka集群进行消息接收确认,则当网络异常、缓冲区满了等情况时,消息可能丢失;

(2)acks=1,同步模式下,只有Leader确认接收成功后但挂掉了,副本没有同步,数据可能丢失;

2、消息消费

Kafka消息消费有两个consumer接口,Low-level API和High-level API:

Low-level API:消费者自己维护offset等值,可以实现对Kafka的完全控制;

High-level API:封装了对parition和offset的管理,使用简单;

如果使用高级接口High-level API,可能存在一个问题就是当消息消费者从集群中把消息取出来、并提交了新的消息offset值后,还没来得及消费就挂掉了,那么下次再消费时之前没消费成功的消息就诡异的消失了;

解决办法:
针对消息丢失:同步模式下,确认机制设置为-1,即让消息写入Leader和Follower之后再确认消息发送成功;异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态;
针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。

Kafka分区分配策略

1、RangeAssignor
RangeAssignor策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。
对于每一个topic,RangeAssignor策略会将消费组内所有订阅这个topic的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。

2、RoundRobinAssignor
RoundRobinAssignor策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询方式逐个将分区以此分配给每个消费者。

如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobinAssignor策略的分区分配会是均匀的。

如果同一个消费组内的消费者所订阅的信息是不相同的,那么在执行分区分配的时候就不是完全的轮询分配,有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个topic,那么在分配分区的时候此消费者将分配不到这个topic的任何分区。

3、StickyAssignor
(1)分区的分配要尽可能的均匀;
(2)分区的分配尽可能的与上次分配的保持相同。
当两者发生冲突时,第一个目标优先于第二个目标。

为什么Kafka不支持读写分离?

在Kafka中,生产者写入消息、消费者读取消息的操作都是与leader副本进行交互的,从而实现的是一种主写主读的生产消费模型。

Kafka并不支持主写从读,因为主写从读有2个很明显的缺点:

(1)数据一致性问题。数据从主节点转到从节点必然会有一个延时的时间窗口,这个时间窗口会导致主从节点之间的数据不一致。某一时刻,在主节点和从节点中A数据的值都为X, 之后将主节点中A的值修改为Y,那么在这个变更通知到从节点之前,应用读取从节点中的A数据的值并不为最新的Y,由此便产生了数据不一致的问题。

(2)延时问题。类似Redis这种组件,数据从写入主节点到同步至从节点中的过程需要经历网络→主节点内存→网络→从节点内存这几个阶段,整个过程会耗费一定的时间。而在Kafka中,主从同步会比Redis更加耗时,它需要经历网络→主节点内存→主节点磁盘→网络→从节点内存→从节点磁盘这几个阶段。对延时敏感的应用而言,主写从读的功能并不太适用。

读写分离适用于那种读负载很大,而写操作相对不频繁的场景,Kafka不属于这样的场景。