kafka中的topic可以细分为不同的partition,一个topic可以将消息存放在不同的partition中。
每个partition可以设置一个leader和多个follower。kafka的消息没有设置读写分离,每个消息发送时,都是发送至对应的partition的leader-paertion,follower-partition主要是为了备份数据而存在,当leader-partition出现故障时,数据已经完全同步的follower-partition也会切换成leader-partition。
AR:分区中所有的副本统称为AR。
ISR:所有与leader节点保持同步的副本(包括leader节点)组成的节点,生产者首先将消息发送给leader副本,然后follower从leader中同步消息。
ISR是AR的子集。
在partition中,一个topic中的数据存放在不同的partition中,一个分区的内容会存储成一个log文件,为了防止log过大,引入了日志分段,根据一定规则将log切分为多个logSegment,相当于一个巨型文件被切分成了很多不同的文件。log和logSegment关系如下:
Log在物理上只以文件夹的形式存储,日志文件在磁盘的存储如下:
在server.properties配置文件中可以指定一个全局的分区数设置,这是对每个主题下的分区数的默认设置,默认是1。
当然每个主题也可以自己设置分区数量,如果创建主题的时候没有指定分区数量,则会使用server.properties中的设置。
bin/kafka-topics.sh --zookeeper localhost:2181 --create --topic my-topic --partitions 2 --replication-factor 1
在创建主题的时候,可以使用--partitions选项指定主题的分区数量
[root@localhost kafka_2.11-2.0.0]# bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic abc
Topic:abc PartitionCount:2 ReplicationFactor:1 Configs:
Topic: abc Partition: 0 Leader: 0 Replicas: 0 Isr: 0
Topic: abc Partition: 1 Leader: 0 Replicas: 0 Isr: 0
kafka使用分区将topic的消息打散到多个分区,分别保存在不同的broker上,实现了producer和consumer消息处理的高吞吐量。
Kafka的producer和consumer都可以多线程地并行操作,而每个线程处理的是一个分区的数据。因此分区实际上是调优Kafka并行度的最小单元。对于producer而言,它实际上是用多个线程并发地向不同分区所在的broker发起Socket连接同时给这些分区发送消息;而consumer,同一个消费组内的所有consumer线程都被指定topic的某一个分区进行消费。
所以说,如果一个topic分区越多,理论上整个集群所能达到的吞吐量就越大。
分区是否越多越好呢?显然也不是,因为每个分区都有自己的开销:
一、客户端/服务器端需要使用的内存就越多
客户端producer有个参数batch.size,默认是16KB。它会为每个分区缓存消息,一旦满了就打包将消息批量发出。
分区越多,consumer端获取数据所需的内存越多。同时consumer线程数要匹配分区数(大部分情况下是最佳的消费吞吐量配置)的话,那么这里面的线程切换的开销本身已经不容小觑了。服务器端的很多组件都在内存中维护了分区级别的缓存,比如controller,FetcherManager等,因此分区数越多,这种缓存的成本就越大。
二、文件句柄的开销
每个分区在底层文件系统都有属于自己的一个目录。该目录下通常会有两个文件: base_offset.log和base_offset.index。Kafak的controller和ReplicaManager会为每个broker都保存这两个文件句柄(file handler)。很明显,如果分区数越多,所需要保持打开状态的文件句柄数也就越多,最终可能会突破你的ulimit -n的限制。
三、降低高可用性
Kafka通过副本(replica)机制来保证高可用。具体做法就是为每个分区保存若干个副本(replica_factor指定副本数)。每个副本保存在不同的broker上。
如果你有10000个分区,10个broker,也就是说平均每个broker上有1000个分区。此时这个broker挂掉了,那么zookeeper和controller需要立即对这1000个分区进行leader选举。比起很少的分区leader选举而言,这必然要花更长的时间,并且通常不是线性累加的。如果这个broker还同时是controller情况就更糟了。
可以遵循一定的步骤来尝试确定分区数:创建一个只有1个分区的topic,然后测试这个topic的producer吞吐量和consumer吞吐量。假设它们的值分别是Tp和Tc,单位可以是MB/s。然后假设总的目标吞吐量是Tt,那么分区数 = Tt / max(Tp, Tc)
说明:Tp表示producer的吞吐量。测试producer通常是很容易的,因为它的逻辑非常简单,就是直接发送消息到Kafka就好了。Tc表示consumer的吞吐量。测试Tc通常与应用的关系更大, 因为Tc的值取决于你拿到消息之后执行什么操作,因此Tc的测试通常也要麻烦一些。
1、要使生产者分区中的数据合理消费,消费者的线程对象和分区数保持一致,多余的线程不会进行消费(会浪费)
2、消费者默认即为一个线程对象 ;
3、达到合理消费最好满足公司:消费者服务器数*线程数 = partition个数
默认的分区策略是:
同一时刻,一条消息只能被组中的一个消费者实例消费。
消费者组订阅一个主题,意味着主题下的所有分区都会被组中的消费者消费到,并且主题下的每个分区只从属于组中的一个消费者,不可能出现组中的两个消费者负责同一个分区。
如果分区数大于或者等于组中的消费者实例数,那么一个消费者会负责多个分区;如果消费者实例的数量大于分区数,有一些消费者是多余的,一直接不到消息而处于空闲状态。
即:
为什么一个消费者可以消费多个分区,但是一个分区不能被多个消费者消费呢?
就是要保证一个分区下的消息顺序性。倘若,两个消费者负责同一个分区,那么就意味着两个消费者同时读取分区的消息,由于消费者自己可以控制读取消息的offset,就有可能C1才读到2,而C1读到1,C1还没处理完,C2已经读到3了,则会造成很多浪费,因为这就相当于多线程读取同一个消息,会造成消息处理的重复,且不能保证消息的顺序,这就跟主动推送(push)无异。
所以说消息积压的时候,部署多台消费者实例是不能加快消费原有分区的消息的。最多增加到和partition数量一致,超过的组员只会占用资源,而不起作用。
kafka官方文档:https://kafka.apache.org/documentation.html#introduction
通过在主题中具有并行性--分区--的概念,Kafka能够为用户进程池提供排序保证和负载平衡。这是通过将主题中的分区分配给使用者组中的使用者来实现的,这样每个分区就会被组中的一个消费者使用。通过这样做,我们确保使用者是该分区的唯一读者,并按顺序使用数据。由于有许多分区,这仍然平衡了许多使用者实例的负载。但是,请注意,不能有比分区更多的使用者实例。
消费者从partition中消费数据,consumer有group的概念,每个group可以消费完整的一份topic中的数据。
是默认的分配策略,是基于每个主题的。
1、range分配策略针对的是主题(这里所说的分区指的某个主题的分区,消费者值的是订阅这个主题的消费者组中的消费者实例)
2、首先,将分区按数字顺序排行序,消费者按消费者名称的字典序排好序
3、然后,用分区总数除以消费者总数。如果能够除尽平均分配;若除不尽,则位于排序前面的消费者将多负责一个分区
假设有3个主题(T1,T2,T3),都有7个分区,那么按照咱们上面这种Range分配策略分配后的消费结果如下:
消费者线程 | 对应消费的分区序号 |
---|---|
C0-0 | T1(0,1,2),T2(0,1,2),T3(0,1,2) |
C1-0 | T1(3,4),T2(3,4),T3(3,4) |
C1-1 | T1(5,6),T2(5,6),T3(5,6) |
RoundRobin策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询算法逐个将分区以此分配给每个消费者。
使用RoundRobin分配策略时会出现两种情况:
因此在使用RoundRobin分配策略时,为了保证得均匀的分区分配结果,需要满足两个条件:
我们分别举例说明:
第一种:比如我们有3个消费者(C0,C1,C2),都订阅了2个主题(T0 和 T1)并且每个主题都有 3 个分区(p0、p1、p2),那么所订阅的所有分区可以标识为T0p0、T0p1、T0p2、T1p0、T1p1、T1p2。此时使用RoundRobin分配策略后,得到的分区分配结果如下:
消费者线程 | 对应消费的分区序号 |
---|---|
C0 | T0p0、T1p0 |
C1 | T0p1、T1p1 |
C2 | T0p2、T1p2 |
第二种:比如我们依然有3个消费者(C0,C1,C2),他们合在一起订阅了 3 个主题:T0、T1 和 T2(C0订阅的是主题T0,消费者C1订阅的是主题T0和T1,消费者C2订阅的是主题T0、T1和T2),这 3 个主题分别有 1、2、3 个分区(即:T0有1个分区(p0),T1有2个分区(p0、p1),T2有3个分区(p0、p1、p2)),即整个消费者所订阅的所有分区可以标识为 T0p0、T1p0、T1p1、T2p0、T2p1、T2p2。此时如果使用RoundRobin分配策略,得到的分区分配结果如下:
消费者线程 | 对应消费的分区序号 |
---|---|
C0 | T0p0 |
C1 | T1p0 |
C2 | T1p1、T2p0、T2p1、T2p2 |
在kafka的0.11.X版本才开始引入的,是目前最复杂也是最优秀的分配策略。
S它的设计主要实现了两个目的,如果这两个目的发生了冲突,优先实现第一个目的:
我们有3个消费者(C0,C1,C2),都订阅了2个主题(T0 和 T1)并且每个主题都有 3 个分区(p0、p1、p2),那么所订阅的所有分区可以标识为T0p0、T0p1、T0p2、T1p0、T1p1、T1p2。此时使用Sticky分配策略后,得到的分区分配结果如下:
消费者线程 | 对应消费的分区序号 |
---|---|
C0 | T0p0、T1p0 |
C1 | T0p1、T1p1 |
C2 | T0p2、T1p2 |
看起来和前面RoundRobin分配策略一样,但其实底层实现并不一样。
这里假设C2故障退出了消费者组,然后需要对分区进行再平衡操作。
如果使用的是RoundRobin分配策略,它会按照消费者C0和C1进行重新轮询分配,再平衡后的结果如下:
消费者线程 | 对应消费的分区序号 |
---|---|
C0 | T0p0、T0p2、T1p1 |
C1 | T0p1、T1p0、T1p2 |
但是如果使用的是Sticky分配策略,再平衡后的结果会是这样:
消费者线程 | 对应消费的分区序号 |
---|---|
C0 | T0p0、T1p0、T0p2 |
C1 | T0p1、T1p1、T1p2 |
Stiky分配策略保留了再平衡之前的消费分配结果,并将原来消费者C2的分配结果分配给了剩余的两个消费者C0和C1,最终C0和C1的分配还保持了均衡。这时候再体会一下sticky(翻译为:粘粘的)这个词汇的意思,是不是豁然开朗了。
对于同一个分区而言有可能之前的消费者和新指派的消费者不是同一个,对于之前消费者进行到一半的处理还要在新指派的消费者中再次处理一遍,这时就会浪费系统资源。而使用Sticky策略就可以让分配策略具备一定的“粘性”,尽可能地让前后两次分配相同,进而可以减少系统资源的损耗以及其它异常情况的发生。
分区Rebalance(再均衡)场景
Rebalance给消费者群组带来了高可用性与伸缩性,但是在Rebalance期间,消费者无法读取消息,整个群组一小段时间不可用,而且当分区被重新分配给另一个消费者时,消费者当前的读取状态会丢失。
如果想实现广播的模式就需要设置多个消费者组,这样当一个消费者组消费完这个消息后,丝毫不影响其他组内的消费者进行消费,这就是广播的概念。
(1)多个消费者组,1个partition
该topic内的数据被多个消费者组同时消费,当某个消费者组有多个消费者时也只能被一个消费者消费,如图4所示:
(2)多个消费者组,多个partition
该topic内的数据可被多个消费者组多次消费,在一个消费者组内,每个消费者又可对应该topic内的一个或者多个partition并行消费,如图5所示:
参考:
Kafka分区与消费者的关系:https://www.cnblogs.com/cjsblog/p/9664536.html
Kafka分区数与消费者个数:https://www.jianshu.com/p/dbbca800f607,https://blog.csdn.net/OiteBody/article/details/80595971
kafka分区和消费者线程的关系:https://blog.csdn.net/tankun940507994/article/details/72781996
深入分析Kafka架构(三):消费者消费方式、三种分区分配策略、offset维护:https://blog.csdn.net/qq_26803795/article/details/105562691
kafka中partition数量与消费者对应关系以及Java实践:https://www.tqwba.com/x_d/jishu/279556.html
为什么不能有比分区更多的使用者实例?:https://stackoverflow.com/questions/25896109/in-apache-kafka-why-cant-there-be-more-consumer-instances-than-partitions
kafka多个消费者消费一个topic_详细解析kafka之 kafka消费者组与重平衡机制:https://blog.csdn.net/weixin_39737224/article/details/112073632