消费者介绍
-
消费者(Consumer)负责订阅Kafka中的主题(Topic),并且从主题上拉取消息。Kafka中还存在消费者组(ConsumerGroup)的概念。每一个消费者都有一个对应的消费者组。当消息发布到主题后,会被投递给订阅它的消费者组中的一个消费者。
Kafka支持两种消息投递模式:点对点(P2P,Point-to-Point)模式和发布订阅(Pub/Sub)模式.
1.点对点模式基于队列,消息生产者发送消息到队列,消费者从队列中接受消息。所有消费者都位于同一个消费者组中,每条消息只会被一个消费者所处理。
2.发布订阅模式在消息一对多广播时采用。所有消费者都属于不同的消费者组,那么所有消息都会广播给所有消费者,每条消息会被所有消费者所处理。
消费方式
-
consumer 采用 pull(拉)模式从 broker 中读取数据。
-
push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的
它的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而 pull模式则可以根据 consumer 的消费能力以适当的速率消费消息。
pull 模式不足之处是,如果 kafka 没有数据,消费者可能会陷入循环中,一直返回空数 据。针对这一点,Kafka 的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有 数据可供消费,consumer 会等待一段时间之后再返回,这段时长即为 timeout。
分区策略
Kafka的分区分配过程
- 确定群组协调器(GroupCoordinator),每当我们创建一个消费组,kafka 会为我们分配一个 broker 作为该消费组的coordinator。
- 注册消费者 并选出 leader consumer,当我们的有了 coordinator 之后,消费者将会开始往该coordinator上进行注册,第一个注册的消费者将成为该消费组的 leader,其他的为 follower.
- 当 leader选出来后,他会从coordinator那里实时获取分区 和consumer信息,并根据分区策略给每个consumer分配分区,并将分配结果告诉 coordinator。
- follower 消费者将从coordinator 那里获取到自己相关的分区信息进行消费,对于所有的 follower 消费者而言,他们只知道自己消费的分区,并不知道其他消费者的存在。
- 至此,消费者都知道自己的消费的分区,分区过程结束,当发生 分区再均衡的时候,leader 将会重复分配过程。
消费者分区分配策略
- Range
将partitions的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。
我们有10个分区,3个消费者线程, 10 / 3 = 3,而且除不尽,那么消费者线程 C1-0 将会多消费一个分区:
C1-0 将消费 0, 1, 2, 3 分区
C2-0 将消费 4, 5, 6 分区
C2-1 将消费 7, 8, 9 分区
- RoundRobin
RoundRobin策略的工作原理:将所有主题的分区组成 TopicAndPartition 列表,然后对 TopicAndPartition 列表按照 hashCode 进行排序,然后通过轮询方式逐个将分区以此分配给每个消费者。
如按照 hashCode 排序完的topic-partitions组依次为T1-5, T1-3, T1-0, T1-8, T1-2,T1-1, T1-4, T1-7, T1-6, T1-9,我们的消费者线程排序为C1-0, C1-1, C2-0, C2-1,最后分区分配的结果为:
C1-0 将消费 T1-5, T1-2, T1-6 分区;
C1-1 将消费 T1-3, T1-1,T1-9 分区;
C2-0 将消费 T1-0, T1-4 分区;
C2-1 将消费 T1-8, T1-7 分区;
消费者和消费者组
- 在 Kafka 中,消费者通常是消费者群组的一部分,多个消费者群组共同读取同一个主题时,彼此之间互不影响。Kafka 之所以要引入消费者群组这个概念是因为 Kafka 消费者经常会做一些高延迟的操作,比如把数据写到数据库或 HDFS,或者进行耗时的计算,在这些情况下,单个消费者无法跟上数据生成的速度。此时可以增加更多的消费者,让它们分担负载,分别处理部分分区的消息,这就是Kafka 实现横向伸缩的主要手段。
需要注意的是:同一个分区只能被同一个消费者群组里面的一个消费者读取,不可能存在同一个分区被同一个消费者群里多个消费者共同读取的情况,如图:
可以看到即便消费者 Consumer5 空闲了,但是也不会去读取任何一个分区的数据,这同时也提醒我们在使用时应该合理设置消费者的数量,以免造成闲置和额外开销。
分区再平衡
因为群组里的消费者共同读取主题的分区,所以当一个消费者被关闭或发生崩溃时,它就离开了群组,原本由它读取的分区将由群组里的其他消费者来读取。同时在主题发生变化时 , 比如添加了新的分区,也会发生分区与消费者的重新分配,分区的所有权从一个消费者转移到另一个消费者,这样的行为被称为再均衡。正是因为再均衡,所以消费费者群组才能保证高可用性和伸缩性。
消费者通过向群组协调器所在的 broker 发送心跳来维持它们和群组的从属关系以及它们对分区的所有权。只要消费者以正常的时间间隔发送心跳,就被认为是活跃的,说明它还在读取分区里的消息。消费者会在轮询消息或提交偏移量时发送心跳。如果消费者停止发送心跳的时间足够长,会话就会过期,群组协调器认为它已经死亡,就会触发再均衡。
附录 : Kafka消费者可选属性
- fetch.min.byte
消费者从服务器获取记录的最小字节数。如果可用的数据量小于设置值,broker 会等待有足够的可用数据时才会把它返回给消费者。- fetch.max.wait.ms broker
返回给消费者数据的等待时间,默认是 500ms。- max.partition.fetch.bytes
该属性指定了服务器从每个分区返回给消费者的最大字节数,默认为 1MB。- session.timeout.ms
消费者在被认为死亡之前可以与服务器断开连接的时间,默认是 3s。- auto.offset.reset
该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
latest (默认值) :在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的最新记录);
earliest:在偏移量无效的情况下,消费者将从起始位置读取分区的记录。- enable.auto.commit
是否自动提交偏移量,默认值是 true。为了避免出现重复消费和数据丢失,可以把它设置为 false。- client.id
客户端 id,服务器用来识别消息的来源。- max.poll.records
单次调用 poll() 方法能够返回的记录数量。- receive.buffer.bytes & send.buffer.byte
这两个参数分别指定 TCP socket 接收和发送数据包缓冲区的大小,-1 代表使用操作系统的默认值。