Kafka架构、组件及核心概念

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35488412/article/details/88615172

目录:

一、什么是Kafka?

      1.1、为什么需要消息系统(Message Queue)

      1.2、常用Message Queue对比

      1.3、Kafka优缺点:

二、kafka架构

      2.1 kafka组件说明:

      2.2 kafka拓扑结构:

      2.3 zookeeper 存储结构

三、核心组件及概念

      3.1、offset

      3.2、message数据

      3.3、partition数据

      3.4、 Producer

      3.5、Consumer

      3.6、replication

      3.7、In-Sync-Replica(ISR)

 

发布 - 订阅消息系统

在发布 - 订阅系统中,消息生产者称为发布者,消息使用者称为订阅者。在发布 - 订阅系统中,消息被保留在主题中。 与点对点系统不同,消费者可以订阅一个或多个主题并使用该主题中的所有消息。 一个现实生活的例子是电视,它发布不同的渠道,如运动,电影,音乐等,任何人都可以订阅自己的频道集,并获得他们订阅的频道时可用。

一、什么是Kafka?

Kafka是由LinkedIn开发的一个分布式发布/订阅的消息系统和一个强大的队列,使用Scala编写,它以可水平扩展和高吞吐率而被广泛使用。 Kafka适合离线和在线消息消费。 Kafka消息保留在磁盘上,并在群集内复制以防止数据丢失。 Kafka构建在ZooKeeper同步服务之上。 它与Apache Storm和Spark非常好地集成,用于实时流式数据分析。一般作为企业大数据分析平台的数据交换枢纽,不同类型的分布式系统可以统一接入到Kafka,实现和hadoop各个组件之间的不同类型数据的实时高效交换。解决了hadoop生态系统中各个组件和其他产品之间缺乏统一的、高效的数据交换中介的问题。

1.1、为什么需要消息系统(Message Queue)

  • 1.解耦:不用关心对方的具体实现,允许独立的扩展或修改两边的处理过程,只需它们遵守同样的接口约束。
  • 2.冗余:消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。
  • 3.扩展性:因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
  • 4.可恢复性:处理消息的进程挂掉后,加入队列中的消息待系统回复后仍可处理。
  • 5.顺序保证:大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka 保证一个 Partition 内的消息的有序性)
  • 6.缓冲能力:有助于控制和优化数据流经过系统的速度。同步调用下游耗时严重的操作,在访问量剧增的情况下,可能导致下游服务崩溃。使用消息队列能够使关键组件顶住突发的访问压力,即使消费者消费信息能力差,但消息可缓存在消息队列中而不会因为突发的超负荷的请求而完全崩溃。 
  • 7.异步通信:消息同步队列需要等待下游服务响应,使用异步可以节省上游响应时间。用户把一个消息放入队列,但并不立即处理它,然后需要的时候再去处理它们。

1.2、常用Message Queue对比

  • RabbitMQ

RabbitMQ是使用Erlang编写的一个开源的消息队列,用在对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量还在其次。本身支持很多的协议,它非常重量级,更适合于企业级的开发。同时实现了Broker构架,这意味着消息在发送给客户端时先在中心队列排队。对路由,负载均衡或者数据持久化都有很好的支持。

  • Redis

Redis是一个基于Key-Value对的NoSQL数据库,可以当做一个轻量级的队列服务来使用。Redis在数据量大的时候入队很慢,Redis出队则无论数据量大小性能都不错。实验表明:入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如果数据大小超过了10K,Redis则慢的无法忍受;出队时,无论数据大小,Redis都表现出非常好的性能,而RabbitMQ的出队性能则远低于Redis。

  • ZeroMQ

ZeroMQ号称最快的消息队列系统,尤其针对大吞吐量的需求场景。只是一个网络编程的Pattern库,将常见的网络请求形式模式化、组件化,不是一个独立的服务进程。ZeroMQ能够实现RabbitMQ不擅长的高级/复杂的队列,但是开发人员需要自己组合多种技术框架,技术上的复杂度是对这MQ能够应用成功的挑战。仅提供非持久性的队列,如果Down机,数据将丢失。

  • ActiveMQ

ActiveMQ是Apache下的一个子项目。 类似于ZeroMQ,它能够以代理人和点对点的技术实现队列。同时类似于RabbitMQ,它少量代码就可以高效地实现高级应用场景。

  • Kafka/Jafka

Kafka是Apache下的一个子项目,是一个高性能跨语言分布式发布/订阅消息队列系统,而Jafka是在Kafka之上孵化而来的,即Kafka的一个升级版。具有以下特性:快速持久化,可以在O(1)的系统开销下进行消息持久化;高吞吐,在一台普通的服务器上既可以达到10W/s的吞吐速率;完全的分布式系统,Broker、Producer、Consumer都原生自动支持分布式,自动实现负载均衡;支持Hadoop数据并行加载,对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka通过Hadoop的并行加载机制统一了在线和离线的消息处理。Apache Kafka相对于ActiveMQ是一个非常轻量级的消息系统,除了性能非常好之外,还是一个工作良好的分布式系统。

备注:ZeroMQ(∅MQ),你们看这名字,说的是没有mq。zmq是支持fanout、topic这些功能的高级socket,不再是传统socket的点到点通信了,于是乎似乎有了mq语义。但是,mq作为一个服务,重要意义是解耦系统中的不同服务,然而zmq不是一个独立服务进程,自然没有mq这种在中间解耦的能力。而kafka和rmq虽然有太多不同的地方,但都是有顺序性的消息存储服务,可以用来解耦各种信息的生产者和消费者。所以,这三个东西不是同一层面的东西,rabbitmq 和 zmq 在名字里都有mq,但不都是mq,不是一个位置的东西;kafka不叫mq是因为它不支持amqp,但实质上还可以当是个mq,和rmq差不多是一个位置的东西。

1.3、Kafka优缺点:

优点:可靠性(分区,容错), 可扩展性 , 耐用性(磁盘-持久), 性能(快、稳定,高吞吐量)

缺点:消息乱序(不能保证全局有序)、不支持事务(可能会丢消息或有重复消息)、复杂性(部署和维护成本更高)

二、kafka架构

2.1 kafka组件说明:

Topic :每条发布到 kafka 集群的消息属于的类别,即 kafka 是面向 topic 的。

Producer:消息生产者,发布消息到 kafka 集群的终端或服务。

consumer:从 kafka 集群中消费消息的终端或服务。

Broker: 集群中的每一个服务器都是一个Broker(代理).

Partition:每个 topic 包含一个或多个 partition。kafka 分配的单位是 partition。

Segment:partition物理上由多个segment组成。

offset : 每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每个消息都有一个连续递增的序列号叫做offset,偏移量offset在每个分区中是唯一的。

replica: partition 的副本,保障 partition 的高可用。

follower: replica 中的一个角色,从 leader 中复制(fentch)数据。

leader: replica 中的一个角色, producer 和 consumer 只跟 leader 交互。

controller:kafka 集群中的其中一个服务器,用来进行 leader election 以及 各种 failover。

zookeeper:kafka 通过 zookeeper 来存储集群的 meta 信息。

Consumer group:high-level consumer API 中,每个 consumer 都属于一个 consumer group,每条消息和partition只能被 consumer group 中的一个 Consumer 消费,但可以被多个 consumer group 消费。

套接字:源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。

2.2 kafka拓扑结构:

2.3 zookeeper 存储结构

kafka 在 zookeeper 中的存储结构如下图所示:

三、核心组件及概念

3.1、offset

对于每个主题,Kafka集群维护一个分区日志,如下所示:

每个分区是一个有序的,不可变的记录序列,不断追加到结构化的提交日志中。分区中的记录每个分配一个连续的id号,称为offset ,用于唯一标识分区内的每条记录。

实际上,以消费者为单位保留的唯一元数据是消费者在日志中的偏移或位置。这个偏移量是由消费者控制的:消费者通常会在读取记录时线性地推进其偏移量,但事实上,由于消费者的位置是由消费者控制的,所以它可以以任何喜欢的顺序消费记录。例如,消费者可以重置为较旧的偏移量以重新处理来自过去的数据,或者跳至最近的记录并从“now”开始消费。

 

3.2、message数据

Partition中的每条Message由offset来表示它在这个partition中的偏移量,这个offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了partition中的一条Message。因此,可以认为offset是partition中Message的id。partition中的每条Message包含了以下三个属性:

  • offset
  • MessageSize
  • data

其中offset为long型,MessageSize为int32,表示data有多大,data为message的具体内容。它的格式和Kafka通讯协议中介绍的MessageSet格式是一致。

3.3、partition数据

思考一下,如果一个partition只有一个数据文件会怎么样?

  1. 新数据是添加在文件末尾(调用FileMessageSet的append方法),不论文件数据文件有多大,这个操作永远都是O(1)的。
  2. 查找某个offset的Message(调用FileMessageSet的searchFor方法)是顺序查找的。因此,如果数据文件很大的话,查找的效率就低。

那Kafka是如何解决查找效率的的问题呢?有两大法宝: 1) 分段 2) 索引。

数据文件的分段

Kafka解决查询效率的手段之一是将数据文件分段,每个partition物理上由多个大小相等的segment组成,顺序读写,以该段中最小的offset命名,文件扩展名为.log。

比如有100条Message,它们的offset是从0到99。假设将数据文件分成5段,第一段为0-19,第二段为20-39,以此类推。这样在查找指定offset的Message的时候,用二分查找就可以定位到该Message在哪个segment段中。

为数据文件建索引

数据文件分段使得可以在一个较小的数据文件中查找对应offset的Message了,但是这依然需要顺序扫描才能找到对应offset的Message。为了进一步提高查找的效率,Kafka为每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为.index。

索引文件中包含若干个索引条目,每个条目表示数据文件中一条Message的索引。索引包含两个部分(均为4个字节的数字),分别为相对offset和position。

  • 相对offset:因为数据文件分段以后,每个数据文件的起始offset不为0,相对offset表示这条Message相对于其所属数据文件中最小的offset的大小。举例,分段后的一个数据文件的offset是从20开始,那么offset为25的Message在index文件中的相对offset就是25-20 = 5。存储相对offset可以减小索引文件占用的空间。
  • position,表示该条Message在数据文件中的绝对位置。只要打开文件并移动文件指针到这个position就可以读取对应的Message了。

ndex文件中并没有为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。

在Kafka中,索引文件的实现类为OffsetIndex,它的类图如下:

稀疏索引的优点是避免文件过大,可直接加载到内存,缺点是不能直接获取文件地址。

kafka查找指定offeset,需要两次二分查找、一次顺序查找。

第一次二分查找segment文件;第二次将对应segment的索引文件加载到内存,二分查找offset范围;第三次顺序查找找到指定offset的存储位置。

一句话,Kafka的Message存储采用了分区(partition),分段(LogSegment)和稀疏索引这几个手段来达到了高效性。

partition的分配

  1. 将所有Broker(假设共n个Broker)和待分配的Partition排序
  2. 将第i个Partition分配到第(i mod n)个Broker上 (这个就是leader)
  3. 将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上

3.4、 Producer

producer 采用 push 模式将消息发布到 broker,每条消息都被 append 到 patition 中,属于顺序写磁盘(顺序写磁盘比随机写内存效率要高,保障 kafka 吞吐率)。

负载均衡

  • 不指定message key:轮询
  • 指定message key : 分区=message.key().hashCode()%partition_num

批量发送

producer缓存buffer数据,减少io次数。(配置文件中触发发送的有两个条件:message数量和时间限制)

消息丢失

  • acks=0 :关闭ack,不需要等待Leader确认接受消息
  • acks=1:消息只需要被Leader接受并确认即可,其他Replica可以进行异步拉取无需立即确认,在保证可靠性的同事又不会把效率拉的很低。
  • acks=all:消息要commit到ISR所有replica后,才可以返回ack,消息发送更安全,但是会增加延迟。

3.5、Consumer

Consumer Group

  • consumer的消费分配单位是 patition。每个 consumer 都属于一个 group,一个 partition 只能被同一个 group 内的一个 consumer 所消费(也就保障了一个消息只能被 group 内的一个 consuemr 所消费),但是多个 group 可以同时消费这个 partition。如果消费组内的消费者如果比partition多的话,那么就会有个别消费者一直空闲。
  • 同一个group下,若consumer多余partition,则有部分consumer无法消费partition。Kafka新版本consumer默认提供了两种分配策略:range和round-robin。当然Kafka采用了可插拔式的分配策略,你可以创建自己的分配器以实现不同的分配策略。

Consumer Rebalance

生产过程中broker要分配partition,消费过程这里,也要分配partition给消费者。类似broker中选了一个controller出来,消费也要从broker中选一个coordinator,用于分配partition。

下面从顶向下,分别阐述一下

  1. 怎么选coordinator。
  2. 交互流程。
  3. reblance的流程。

选coordinator

  1. 看offset保存在那个partition
  2. 该partition leader所在的broker就是被选定的coordinator

这里我们可以看到,consumer group的coordinator,和保存consumer group offset的partition leader是同一台机器。

交互流程

把coordinator选出来之后,就是要分配了,整个流程是这样的:

  1. consumer启动、或者coordinator宕机了,consumer会任意请求一个broker,发送ConsumerMetadataRequest请求,broker会按照上面说的方法,选出这个consumer对应coordinator的地址。
  2. consumer 发送heartbeat请求给coordinator,返回IllegalGeneration的话,就说明consumer的信息是旧的了,需要重新加入进来,进行reblance。返回成功,那么consumer就从上次分配的partition中继续执行。

reblance流程

  1. consumer给coordinator发送JoinGroupRequest请求。
  2. 这时其他consumer发heartbeat请求过来时,coordinator会告诉他们,要reblance了。
  3. 其他consumer发送JoinGroupRequest请求。
  4. 所有记录在册的consumer都发了JoinGroupRequest请求之后,coordinator就会在这里consumer中随便选一个leader。然后回JoinGroupRespone,这会告诉consumer你是follower还是leader,对于leader,还会把follower的信息带给它,让它根据这些信息去分配partition
  5. consumer向coordinator发送SyncGroupRequest,其中leader的SyncGroupRequest会包含分配的情况。
  6. coordinator回包,把分配的情况告诉consumer,包括leader。

当partition或者消费者的数量发生变化时,都得进行reblance。

列举一下会reblance的情况:

  • 增加partition
  • 增加消费者、消费者主动关闭、消费者宕机了
  • coordinator宕机了

传输保证

如果将 consumer 设置为 autocommit,consumer 一旦读到数据立即自动 commit。如果只讨论这一读取消息的过程,那 Kafka 确保了 Exactly once。但实际使用中应用程序并非在 consumer 读取完数据就结束了,而是要进行进一步处理,而数据处理与 commit 的顺序在很大程度上决定了

kafka支持3种消息投递语义

At most once:最多一次,消息可能会丢失,但不会重复

At least once:最少一次,消息不会丢失,可能会重复

Exactly once:只且一次,消息不丢失不重复,只且消费一次(0.11中实现,仅限于下游也是kafka)

在业务中,常常都是使用At least once的模型,如果需要可重入的话,往往是业务自己实现。

消费方式

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

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

存储策略

无论消息是否被消费,kafka 都会保留所有消息。有两种策略可以删除旧数据:

  • 1. 基于时间:log.retention.hours=168
  • 2. 基于大小:log.retention.bytes=1073741824

3.6、replication

  • 副本的基本单元是partition
  • 一个partition的副本个数不会超过broker个数
  • 一个broker最多只有某个partition的一个副本
  • 所有消息的生产、消费都是从partition leader上来处理的,follower只负责复制数据进行备份

3.7、In-Sync-Replica(ISR)

Kafka的判断是否剔除落后节点

  1. 节点必须和zookeeper保持心跳连接
  2. 如果是follower,必须从leader上复制数据过来备份,并且不能落后太多。(这里所描述的“落后太多”指Follower复制的消息落后于Leader后的条数超过预定值,broker配置通过replica.lag.max.messages配置,其默认值是4000或者Follower超过一定时间通过replica.lag.time.max.ms来配置,其默认值是10000,未向Leader发送fetch请求。)

满足上面两个条件认为是in-sync(同步中),成为In-Sync-Replica(ISR),Zookeeper维护partition的ISR数据,理想情况下,ISR包含partition所有的broker节点信息。

ISR的可靠性级别由Producer决定:

request.required.acks

  • 1(默认):ISR中的leader成功接收数据并得到确认后发送下一条消息。
  • 0:无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是可靠性最低。
  • -1:producer需要等待ISR中所有的follower都确认接收到数据才算一次发送完成,可靠性最高,传输效率降低。

注:当ISR成员follower减少为0,只剩leader时就成了ack=1的情况,为提高数据可靠性,在设置request.required.acks=-1的同时,设置min.insync.replicas(ISR最小副本数量),当ISR中副本数量少于最小设置时,客户端会返回NotEnougghReplicasException。

消息不可丢失的设置:

  1. 同步sync(kafka默认同步,即producer.type=sync)的发送模式。
  2. replication.factor>=2(partition的默认replication数量)
  3. min.insync.replicas>=2(ISR最小副本数)
  4. request.required.acks=-1   

现在已经有许多kafka的好文章,本文也引用参考不少,现推荐链接如下:

猜你喜欢

转载自blog.csdn.net/qq_35488412/article/details/88615172