Kafka

来自智得网
跳转至: 导航、​ 搜索

简介

Kafka体系结构

消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件。

kafka是apache旗下一款著名的消息队列。其最早由LinkedIn公司采用Scala语言开发,支持多分区,多副本,kafka以高吞吐,可水平扩展,支持流式数据处理等多种特性而被广泛使用。

kafka的体系概念有几个重要的术语。

Producer,生产者,也就是消息的发送方。

Consumer,消费者,连接kafka集群,接受消息,并进行业务逻辑处理。

Broker,服务节点,可以看作一个kafka实例。

Topic,是一个逻辑概念,Producer和Consumer都会关联特定的主题进行发送和消费。一个Topic还可以分为若干个分区,称为Partition,每个Partition对应不同的物理存储和日志文件。

原理

存储设计

kafka的因为顺序写的特点,所以存储使用日志。每个分区副本下的日志文件会分为多个分段,LogSegment,每个LogSegment有一个日志文件和两个索引文件构成,两个索引文件分别是对偏移量和时间戳的索引。

延迟消息

时间轮的概念

kafka存在大量的延时操作,例如延时生产,延时消费,延时拉取等,kafka基于时间轮的概念实现此类需求。

时间轮是一个存储定时任务的环形队列,队列中每一项代表了该时间范围内需要触发的任务。

时间轮是可以复用的,因为定时任务可能延时较久,所以时间轮有多个级别。比如最小粒度的时间轮间隔时间为1ms,时间轮的格子为20个,则该时间轮可以表示的时间跨度为20ms,那么其上级时间轮的每个格子代表的时间跨度就是20ms,20个格子可以代表400ms的时间范围,以此类推,再上级的时间轮可以表示8000ms的时间范围,如果一个任务需要在8903ms后运行,则首先放在第三级别的时间轮里,到时间后发现任务还有903ms运行,则放入400ms的时间轮,结束之后发现还有403ms,再放入400ms的时间轮,在之后放入第一层的时间轮执行,等3个周期过去就可以执行了。

集群

kafaka的集群节点可以分为控制节点Controller以及数据节点,数据节点又可以分为Leader节点,以及Follow节点。

控制节点Controller是从Broker中选举产生的,选举成功的控制节点会在zookeeper把自身的brokerId写入/controller这个临时节点。控制节点作为 Broker 集群的管理者,管理所有的集群信息和元数据信息。它的职责包括下面几部分:

  • 处理 Broker 节点的上线和下线,包括自然下线、宕机和网络不可达导致的集群变动,Controller 需要及时更新集群元数据,并将集群变化通知到所有的 Broker 集群节点;
  • 创建 Topic 或者 Topic 扩容分区,Controller 需要负责分区副本的分配工作,并主导 Topic 分区副本的 Leader 选举。
  • 管理集群中所有的副本和分区的状态机,监听状态机变化事件,并作出相应的处理。Kafka 分区和副本数据采用状态机的方式管理,分区和副本的变化都在状态机内会引起状态机状态的变更,从而触发相应的变化事件。

kafka提供了多副本的容灾能力,但是副本平时不提供对外服务,无论是发送还是消费。分区中所有的副本(AR)在平时都会和Leader节点保持消息同步,保持消息同步的节点(仍然可能存在滞后,但是在阈值范围内)称为ISR,而消息滞后过多的节点称为OSR。

多副本还有两个重要的概念,HW指可以提供消费者进行消费的位置+1,LEO指下条消息的位置。

性能优化

Kafka使用的零拷贝技术

Kafka主要使用场景在高并发消费以及大量数据批量写入的场景,所以Kafka的性能瓶颈主要在于IO和网络。

因为kafka的架构以及场景,所以写操作都是顺序写(追加写)数据,因为没有随机写引发的寻址操作,Kafka又较强的数据存储能力。

除了磁盘IO之外,高并发场景下网络IO的优化也非常重要。Kafka采用了零拷贝的技术优化网络IO。零拷贝可以有效的减少网络交互中数据的肤质操作,也减少了上下文交换。

Kafka使用sendfile()实现零拷贝功能。

sendfile的实现机制如下:

  1. sendfile() 系统调用利用 DMA 引擎将文件中的数据复制到操作系统内核缓冲区,然后数据被直接复制到与 socket 相关的内核缓冲区中去。
  2. DMA 引擎将数据从内核 socket 缓冲区中复制到协议栈。
  3. 如果在用户调用 sendfile () 系统调用进行数据传输的过程中有其他进程截断了该文件,那么 sendfile () 系统调用会简单地返回给用户应用程序中断前所传输的字节数,errno 会被设置为 success。如果在调用 sendfile() 之前操作系统对文件加上了租借锁,那么 sendfile() 的操作和返回状态将会和 mmap()/write () 一样。