消息队列kafka(下) | 青训营笔记

107 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第7天

前面文章记录看kafka的大部分内容,但是由于kafka内容众多,最后还剩余消费者的一些笔记知识,最后在这篇文章进行消息对列kafka的最后讲解。

Kafka 消费者

Kafka 消费方式

  1. pull(拉)模 式:consumer采用从broker中主动拉取数据。Kafka采用这种方式。
  2. push(推)模式:Kafka没有采用这种方式,因为由broker决定消息发送速率,很难适应所有消费者的消费速率。例如推送的速度是50m/s,Consumer1、Consumer2就来不及处理消息。

注意:pull模式不足之处是,如 果Kafka没有数据,消费者可能会陷入循环中,一直返回空数据。

Kafka 消费者工作流程

Consumer Group(CG):消费者组,由多个consumer组成。形成一个消费者组的条件,是所有消费者的groupid相同。

• 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费。

• 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。

image.png 1、coordinator:辅助实现消费者组的初始化和分区的分配。

coordinator节点选择 = groupid的hashcode值 % 50( __consumer_offsets的分区数量) 例如: groupid的hashcode值 = 1,1% 50 = 1,那么__consumer_offsets 主题的1号分区,在哪个broker上,就选择这个节点的coordinator 作为这个消费者组的老大。消费者组下的所有的消费者提交offset的时候就往这个分区去提交offset。

image.png

image.png 消费者重要参数

  • bootstrap.servers:向 Kafka 集群建立初始连接用到的 host/port 列表
  • key.deserializer和value.deserializer指定接收消息的 key 和 value 的反序列化类型。一定要写全类名。
  • group.id标记消费者所属的消费者组。
  • enable.auto.commit默认值为 true,消费者会自动周期性地向服务器提交偏移量。
  • auto.commit.interval.ms如果设置了 enable.auto.commit 的值为 true, 则该值定义了消费者偏移量向 Kafka 提交的频率,默认 5s。
  • auto.offset.reset:当 Kafka 中没有初始偏移量或当前偏移量在服务器中不存在(如,数据被删除了),该如何处理? earliest:自动重置偏移量到最早的偏移量。 latest:默认,自动重置偏移量为最新的偏移量。 none:如果消费组原来的(previous)偏移量 不存在,则向消费者抛异常。 anything:向消费者抛异常。
  • offsets.topic.num.partitions: __consumer_offsets 的分区数,默认是 50 个分区。
  • session.timeout.ms:Kafka 消费者和 coordinator 之间连接超时时间,默认 45s。 超过该值,该消费者被移除,消费者组执行再平衡。

消费者 API

消费者组案例 1)需求:测试同一个主题的分区数据,只能由一个消费者组中的一个消费。 2)案例实操

import org.apache.kafka.clients.consumer.ConsumerConfig;

import org.apache.kafka.clients.consumer.ConsumerRecord;

import org.apache.kafka.clients.consumer.ConsumerRecords;

import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.time.Duration;

import java.util.ArrayList;

import java.util.Properties;

public class CustomConsumer1 {

public static void main(String[] args) {

// 1.创建消费者的配置对象

Properties properties = new Properties();

// 2.给消费者配置对象添加参数

properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,

"hadoop102:9092");

// 配置序列化 必须

properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,

StringDeserializer.class.getName());

properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,

StringDeserializer.class.getName());

// 配置消费者组 必须

properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
// 创建消费者对象

KafkaConsumer<String, String>

kafkaConsumer

= new

KafkaConsumer<String, String>(properties);

// 注册主题

ArrayList<String> topics = new ArrayList<>();

topics.add("first");

kafkaConsumer.subscribe(topics);

// 拉取数据打印

while (true) {

// 设置 1s 中消费一批数据

ConsumerRecords<String, String> consumerRecords =

kafkaConsumer.poll(Duration.ofSeconds(1));

// 打印消费到的数据

for (ConsumerRecord<String, String> consumerRecord :

consumerRecords) {

System.out.println(consumerRecord);

}
}
}
}

2)启动代码中的生产者发送消息,在 IDEA 控制台即可看到两个消费者在消费不同分区的数据(如果只发生到一个分区,可以在发送时增加延迟代码 Thread.sleep(2);)

ConsumerRecord(topic = first, partition = 0, leaderEpoch = 2, 
offset = 3, CreateTime = 1629169606820, serialized key size = -1, 
serialized value size = 8, headers = RecordHeaders(headers = [], 
isReadOnly = false), key = null, value = hello1)
ConsumerRecord(topic = first, partition = 1, leaderEpoch = 3, 
offset = 2, CreateTime = 1629169609524, serialized key size = -1, 
serialized value size = 6, headers = RecordHeaders(headers = [], 
isReadOnly = false), key = null, value = hello2)
ConsumerRecord(topic = first, partition = 2, leaderEpoch = 3, 
offset = 21, CreateTime = 1629169611884, serialized key size = -1, 
serialized value size = 6, headers = RecordHeaders(headers = [], 
isReadOnly = false), key = null, value = hello3)

生产经验——分区的分配以及再平衡

1.Range 以及再平衡

首先对同一个 topic 里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。假如现在有 7 个分区,3 个消费者,排序后的分区将会是0,1,2,3,4,5,6;消费者排序完之后将会是C0,C1,C2。例如,7/3 = 2 余 1 ,除不尽,那么 消费者 C0 便会多消费 1 个分区。 8/3=2余2,除不尽,那么C0和C1分别多消费一个。

通过 partitions数/consumer数 来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者将会多消费 1 个分区。分区分配策略之Range

注意:如果只是针对 1 个 topic 而言,C0消费者多消费1个分区影响不是很大。但是如果有 N 多个 topic,那么针对每个 topic,消费者 C0都将多消费 1 个分区,topic越多,C0消 费的分区会比其他消费者明显多消费 N 个分区。

容易产生数据倾斜!

2.RoundRobin 以及再平衡

RoundRobin 针对集群中所有Topic而言。RoundRobin 轮询分区策略,是把所有的 partition 和所有的consumer 都列出来,然后按照 hashcode 进行排序,最后通过轮询算法来分配 partition 给到各个消费者。

image.png

3.Sticky 以及再平衡

粘性分区定义:可以理解为分配的结果带有“粘性的”。即在执行一次新的分配之前,考虑上一次分配的结果,尽量少的调整分配的变动,可以节省大量的开销。粘性分区是 Kafka 从 0.11.x 版本开始引入这种分配策略,首先会尽量均衡的放置分区到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分 区不变化。

生产经验——数据积压(消费者如何提高吞吐量)

1)如果是Kafka消费能力不足,则可以考虑增加Topic的分区数,并且同时提升消费组的消费者数量,消费者数 = 分区数。(两者缺一不可)。 2)如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间 < 生产速度),使处理的数据小于生产的数据,也会造成数据积压。

结束

到此为止,已经完成消息队列kafka的全部学习记录,加油~