kafka01-kafka简介
文章目录
一、kafka简介
Kafka是一个开源的分布式流处理平台,最初由LinkedIn开发并捐赠给Apache软件基金会。它被设计为高吞吐量、可扩展、持久化、分布式的发布-订阅消息系统。
Kafka 的核心特点和概念
-
发布-订阅模型:Kafka使用发布-订阅模型,其中消息的生产者将消息发布到一个或多个主题(Topic),而消费者则订阅一个或多个主题来接收消息。
-
分布式架构:Kafka采用分布式的架构,允许将消息分布在多个服务器(称为Broker)上。每个Broker可以容纳多个主题的多个分区(Partition)。
-
高吞吐量:Kafka被设计为具有高吞吐量的系统。它能够处理大量的消息,并能够水平扩展以适应负载的增加。
-
持久化:Kafka使用磁盘存储消息,这使得消息可以被持久化保存,并且可以根据需要进行回放(通过保留一段时间内的消息)。
-
分区和复制:每个主题可以被分为多个分区,每个分区可以在多个Broker之间进行复制,以提供数据冗余和容错性。
-
可靠性:Kafka采用副本机制来提供数据的可靠性。副本可以在多个Broker之间同步,并且在主节点失败时自动进行故障转移。
-
批量处理:Kafka支持批量处理消息,这意味着生产者可以将多个消息一次性发送到Kafka,从而提高吞吐量和效率。
-
流处理:Kafka提供了流处理功能,可以对流式数据进行实时处理和分析,支持常见的流处理操作,如转换、聚合、过滤等。
Kafka应用场景
Kafka广泛应用于各种场景,包括日志收集、事件驱动架构、实时数据流处理、消息队列、指标监控等。它提供了丰富的客户端库,支持多种编程语言,包括Java、Python、Go等,使得开发者能够轻松地与Kafka集成并构建自己的应用程序。
总之,Kafka是一个高性能、可靠的分布式流处理平台,具有强大的消息传递能力和可扩展性,适用于构建实时数据处理和消息驱动的应用程序。
常见消息中间件对比
| 特性 | Kafka | RabbitMQ | RocketMQ | ActiveMQ |
|---|---|---|---|---|
| 开发语言 | Scala&java | Erlang | Java | Java |
| 单击吞吐量 | 十万级 | 万级 | 十万级 | 万级 |
| 时效性 | ms级以内 | us(微秒)级 | ms级 | ms级 |
| 可用性 | 非常高(分布式架构) | 高(主从架构) | 非常高(分布式架构) | 高(主从架构) |
| 消息可靠性 | 经过参数优化配置,可做到0丢失 | 基本不丢失 | 经过参数优化配置,可做到0丢失 | 有较低概率丢失数据 |
| 消息批量操作 | 支持 | 不支持 | 支持 | 支持 |
kafka架构
二、Kafka中的术语解释
Producer(生产者)
生产者即数据的发布者,该角色将消息发布到Kafka的topic中。broker接收到生产者发送的消息后,broker将该消息追加到对应的segment文件中。生产者发送的消息,存储到一个partition中,生产者也可以指定数据存储的partition。
Consumer(消费者)
消费者可以从broker中读取数据。消费者可以消费多个topic中的数据。
Consumer Group(消费者组)
每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)。
broker(服务节点)
Kafka 集群包含一个或多个服务,服务节点称为broker。
broker存储topic的数据。如果某topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。
如果某topic有N个partition,集群有(N+M)个broker,那么其中有N个broker存储该topic的一个partition,剩下的M个broker不存储该topic的partition数据。
如果某topic有N个partition,集群中broker数目少于N个,那么一个broker存储该topic的一个或多个partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致Kafka集群数据不均衡。
Topic(主题)
每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)
类似于数据库的表名
Partition(分区)
topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使用多个segment文件存储。同一个topic下的同一个partition中的数据是有序的,但不同partition间的数据是无序的。如果topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1。
-
增加分区数
当主题中的消息包含有key时(即key不为null),根据key来计算分区的行为就会有所影响。当topic-config的分区数为1时,不管消息的key为何值,消息都会发往这一个分区中;当分区数增加到3时,那么就会根据消息的key来计算分区号,原本发往分区0的消息现在有可能会发往分区1或者分区2中。如此还会影响既定消息的顺序,所以在增加分区数时一定要三思而后行。对于基于key计算的主题而言,建议在一开始就设置好分区数量,避免以后对其进行调整。
-
减少分区数
目前Kafka只支持增加分区数而不支持减少分区数。比如我们再将主题topic-config的分区数修改为1,就会报出InvalidPartitionException的异常。因为减少分区,kafka不知道要如何处理分区里的数据。如果一定要不顾一切代价,减少某个主题分区的数量,需要考虑的因素有很多,比如删除掉的分区中的消息该作何处理?如果随着分区一起消失则消息的可靠性得不到保障;如果需要保留则又需要考虑如何保留。直接存储到现有分区的尾部,消息的时间戳就不会递增,如此对于Spark、Flink这类需要消息时间戳(事件时间)的组件将会受到影响;如果分散插入到现有的分区中,那么在消息量很大的时候,内部的数据复制会占用很大的资源,而且在复制期间,此主题的可用性又如何得到保障?与此同时,顺序性问题、事务性问题、以及分区和副本的状态机切换问题都是不得不面对的。反观这个功能的收益点却是很低,如果真的需要实现此类的功能,完全可以重新创建一个分区数较小的主题,然后将现有主题中的消息按照既定的逻辑复制过去即可。或者把这个主题先删除掉,再重新创建。这样一来,主题里原有的数据就全部丢失了。
虽然分区数不可以减少,但是分区对应的副本数是可以减少的,这个其实很好理解,你关闭一个副本时就相当于副本数减少了。不过正规的做法是使用kafka-reassign-partition.sh脚本来实现,具体用法可以自行搜索。
Partition Replicas(分区副本)
kafka 可以配置 partitions 需要备份的个数(replicas),每个 partition 将会被备份到多台机器上,以提高可用性,备份的数量可以通过配置文件指定。
这种冗余备份的方式在分布式系统中是很常见的,那么既然有副本,就涉及到对同一个文件的多个备份如何进行管理和调度。kafka 采取的方案是:每个 partition 选举一个分区副本作为“leader”,由 leader 负责所有对该分区的读写,其他分区副本作为 follower 只需要简单的与 leader 同步,保持跟进即可。处于同步状态的副本叫做in-sync-replicas(ISR)。
如果原来的 leader 失效,会重新选举其他的 follower 来成为新的 leader。至于如何选取 leader,实际是Kafka 使用 ZK 在 Broker 中选出一个 Controller,用于 Partition 分配和 Leader 选举。另外作为 leader 的分区副本承担了该分区所有的读写请求,follower不与消费者交互,因此作为leader压力是比较大的,从实际情况出发,我们在部署kafka的时候会采用多个broker,为topic创建多个分区来分摊服务压力,这也就意味着有多少个 partition 就会有多少个leader,kafka 会将 leader 分散到不同的 broker 上(同一个副本因子不能放在同一个broker中),确保整体的负载均衡。
Leader
每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。
Follower
Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。
Offset(偏移量)
一个分区对应一个磁盘上的文件,而消息在文件中的位置就称为 offset(偏移量),offset 为一个 long 型数字,它可以唯一标记一条消息。由于kafka 并没有提供其他额外的索引机制来存储 offset,文件只能顺序的读写,所以在kafka中几乎不允许对消息进行“随机读写”。
三、kafka整体数据流程
生产过程
producer 将会和Topic下所有 partition leader 保持 socket 连接,消息由 producer 直接通过 socket 发送到 broker。其中 partition leader 的位置( host : port )注册在 zookeeper 中,producer 作为 zookeeper client,已经注册了 watch 用来监听 partition leader 的变更事件,因此,可以准确的知道谁是当前的 leader。
producer 端采用异步发送:将多条消息暂且在客户端 buffer 起来,并将他们批量的发送到 broker,小数据 IO 太多,会拖慢整体的网络延迟,批量延迟发送事实上提升了网络效率。
对于生产者要写入的一条记录,可以指定四个参数:分别是 topic、partition、key 和 value,其中 topic 和 value(要写入的数据)是必须要指定的,而 key 和 partition 是可选的。对于一条记录,先对其进行序列化,然后按照 Topic 和 Partition,放进对应的发送队列中。如果 Partition 没填,那么情况会是这样的:
a、Key 有填。按照 Key 进行哈希,相同 Key 去一个 Partition。
b、Key 没填。Round-Robin 来选 Partition。
消费过程
在 kafka 中,采用了 pull 方式,即 consumer 在和 broker 建立连接之后,主动去 pull(或者说 fetch )消息,kafka为consumer提供当前topic的offset, consumer 端根据自己的消费能力适时的去 pull消息并处理,且可以控制消息消费的进度(offset)。
对于消费者,不是以单独的形式存在的,每一个消费者属于一个 consumer group,一个 group 包含多个 consumer。特别需要注意的是:订阅 Topic 是以一个消费组来订阅的,发送到 Topic 的消息,只会被订阅此 Topic 的每个 group 中的一个 consumer 消费。如果所有的 Consumer 都具有相同的 group,那么就像是一个点对点的消息系统;如果每个 consumer 都具有不同的 group,那么消息会广播给所有的消费者。具体说来,是根据 partition 来分的,一个 Partition,只能被消费组里的一个消费者消费,但是可以同时被多个消费组消费,消费组里的每个消费者是关联到一个 partition 的,因此有这样的说法:对于一个 topic,同一个 group 中不能有多于 partitions 个数的 consumer 同时消费,否则将意味着某些 consumer 将无法得到消息。同一个消费组的两个消费者不会同时消费一个 partition。
四、kafka消费者再均衡
1. 什么是再均衡
再均衡( Rebalance )本质上是一种协议,规定了一个消费组中所有消费者如何达成一致来分配订阅主题的每个分区。比如某个消费组有20个消费组,订阅了一个具有100个分区的主题。正常情况下,Kafka 平均会为每个消费者分配5 个分区。这个分配的过程就叫再均衡。
2. 触发时机
-
组成员发生变更 ( 新消费者加入消费组组、已有消费者主动离开或崩溃了 )
-
订阅主题数发生变更。如果正则表达式进行订阅,则新建匹配正则表达式的主题触发再均衡。
-
订阅主题的分区数发生变更
3. 消费者分区分配策略
RoundRobinAssignor、RangeAssignor、StickyAssignor、CooperativeStickyAssignor(kafka2.4.0版本引入的策略)
kafka可以同时使用多个分区分配策略
五、kafka消息存储物理结构
kafka放弃的Java的堆存储,改为使用磁盘(使用文件系统和操作系统的页缓存),同时将随机写改为顺序写,建立在文件追加的基础上,极大提高io性能。
Kafka中每个topic的消息被一个或多个分区(Partition)管理,它作为消息管理名义上最大的管家其实是由很多的 Segment 文件组成。如果一个 Partition 是一个单个非常长的文件的话,那么这个查找操作会非常慢并且容易出错。为解决这个问题,Partition 又被划分成多个 Segment 来组织数据。Segment 并不是终极存储,在它的下面还有两个组成部分:
- 索引文件:以 .index 后缀结尾,存储当前数据文件的索引;
- 数据文件:以 .log 后缀结尾,存储当前索引文件名对应的数据文件。
索引文件中元数据指向对应数据文件中message的物理偏移地址,比如5,1147代表数据文件中的第5个message,它的物理偏移地址为1147,再来看数据文件中,Message 258482表示:在全局partiton中是第258482个message。
Segment 文件的命名规则是: 某个 Partition 全局的第一个 Segment 从 0 开始,后续每个 Segment 文件名以当前 Partition 的最大 offset(消息偏移量)为基准,文件名长度为 64 位 long 类型,19 位数字字符长度,不足部分用 0 填充。
文章作者 necor 上次更新 2024-01-08