背景
简化的系统拓扑图如下
问题
大量增加了传感器数量后,发现数据仓库里的数据入库有延迟了,
于是使用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
2023-04-27 08:50
发现十分钟就有三百多万数据的积压,约平均每分钟319,799条积压数据
说明消费者消费能力不足了,所以我们应该增加消费者组中的的数量,
这里就要提一下分区与消费者组以及消费者之间的关系了:
- 对于同一个topic, 不同消费者组可以消费同一个分区中的数据, 也就是他们之间是互不影响,重复消费的
- 而同一个消费者组下面不同的消费者之间是合作关系,也就是说一个分区只会被一个消费者消费,
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
11:40
平均约每分钟积压13,439.4条
可以看出,10分钟消息积压数量有所下降.但还不够.
第三次试验,分区数增加到6, 消费者数增加到6
结果如下
13:50
14:00
可以看出这次消息一旦发出,几乎立即就被消费掉了, 10分钟几乎没有产生积压
思考
这样问题就解决了吗?
这样改动又会产生新的问题:
- 时序性问题
我们传感器是分组分批次传数据的, 不同组之间是独立的. 原来只有一个分区的时候,数据是可以保证时序性没问题. 但现在多个分区了,每个分区消费速度也是互相独立的, 那么怎么去保证数据入库后总体的时序性呢?
在生产者发送消息时,可以传给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);
}
- 数据倾斜问题
万一某个组产生的数据特别多,就会导致某一个分区数据特别多,造成数据倾斜
那么可以更改key值,直接以要求时序性的最小单位:传感器号来作为key值,因为每个单独的传感器发出的消息是独立,持续且时序的,并且传感器的编号是连续的,不容易出现很多传感器号的hash值都相同, 这样就能保证数据又时序,又能平均分布在不同的分区了.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?