背景

简化的系统拓扑图如下
image

问题

大量增加了传感器数量后,发现数据仓库里的数据入库有延迟了,
于是使用kafka自带的bin目录下的kafka-consumer工具查看一下消费者消息积压数量
[pmish@localhost bin]$ ./kafka-consumer-groups.sh --bootstrap-server 10.168.205.81:9092 --group k2h --describe
当然也可以使用offsetExplorer工具查看.
查看后发现当前消费的offset和最后一条的offset差距很大,说明有大量的数据积压

那么开始试验.

第一次试验,模拟出问题的环境的配置并且增加数据的发送量, topic是默认的1个分区,消费者1个,生产者的生产效率是5760条/秒

单分区单消费者,试验topic: formation
2023-04-27 08:40
image

2023-04-27 08:50
image

发现十分钟就有三百多万数据的积压,约平均每分钟319,799条积压数据
说明消费者消费能力不足了,所以我们应该增加消费者组中的的数量,

这里就要提一下分区与消费者组以及消费者之间的关系了:
  1. 对于同一个topic, 不同消费者组可以消费同一个分区中的数据, 也就是他们之间是互不影响,重复消费的
  2. 而同一个消费者组下面不同的消费者之间是合作关系,也就是说一个分区只会被一个消费者消费,
    a. 如果分区多于消费者数量,那么就有的消费者会消费多个分区,
    b. 如果分区数量少于消费者数量,就会有消费者空闲下来,不消费分区.

所以我们既然要增加消费者, 也要相应地增加topic的分区数

第二次试验,topic增加分区到3, 消费者增加到3

使用kafka-topic工具增加topic的分区数:

kafka的bin目录下执行
./kafka-topics.sh --zookeeper 10.168.205.81:2181 --alter --topic division --partitions 3
查看增加的结果

[pmish@localhost bin]$ ./kafka-topics.sh --zookeeper 10.168.205.81:2181 --describe --topic division
Topic: division	PartitionCount: 3	ReplicationFactor: 1	Configs: 
	Topic: division	Partition: 0	Leader: 0	Replicas: 0	Isr: 0
	Topic: division	Partition: 1	Leader: 0	Replicas: 0	Isr: 0
	Topic: division	Partition: 2	Leader: 0	Replicas: 0	Isr: 0

增加消费者组中消费者数量

这里使用的java代码,依赖是

<dependency>
	<groupId>org.springframework.kafka</groupId>
	<artifactId>spring-kafka</artifactId>
	<version>2.7.7</version>
</dependency>

增加java消费者,只需要把concurrency从1增加到3

@KafkaListener(id = "k2h-division", idIsGroup = false, topics = "division", concurrency = "3", errorHandler = "myErrorHandler")
    public void divisionRecord(List<ConsumerRecord<String, String>> consumerRecords, Acknowledgment acknowledgment);

结果如下:

3分区,3消费者, 试验topic:division
11:30
image

11:40
image
平均约每分钟积压13,439.4条

可以看出,10分钟消息积压数量有所下降.但还不够.

第三次试验,分区数增加到6, 消费者数增加到6

结果如下
13:50
image
14:00
image
可以看出这次消息一旦发出,几乎立即就被消费掉了, 10分钟几乎没有产生积压

思考

这样问题就解决了吗?
这样改动又会产生新的问题:

  1. 时序性问题
    我们传感器是分组分批次传数据的, 不同组之间是独立的. 原来只有一个分区的时候,数据是可以保证时序性没问题. 但现在多个分区了,每个分区消费速度也是互相独立的, 那么怎么去保证数据入库后总体的时序性呢?
    在生产者发送消息时,可以传给kafka一个key值,对key值进行hash来确定这条数据具体进入哪个分区,我们可以根据这个特性来把同一组的数据赋予同一个key值,即把组号作为key值, 这样有时序性要求的同组的数据,就会进入同一个分区,就确保了数据的时序性.
    在java中指定key值:
    依赖
<dependency>
	<groupId>org.springframework.kafka</groupId>
	<artifactId>spring-kafka</artifactId>
	<version>2.7.7</version>
</dependency>

java代码,其中变量key

public void send(FormationData f) {
        String jsonStr;
        jsonStr = JSON.toJSONString(f);
		String key = f.getBatchNo();
        kafkaTemplate.send(topicname, key ,jsonStr);
        log.info("已发送信息"+ jsonStr);
    }
  1. 数据倾斜问题
    万一某个组产生的数据特别多,就会导致某一个分区数据特别多,造成数据倾斜
    那么可以更改key值,直接以要求时序性的最小单位:传感器号来作为key值,因为每个单独的传感器发出的消息是独立,持续且时序的,并且传感器的编号是连续的,不容易出现很多传感器号的hash值都相同, 这样就能保证数据又时序,又能平均分布在不同的分区了.
posted on 2023-04-27 15:46  一贯可乐  阅读(700)  评论(0编辑  收藏  举报



123