1 背景
业务上遇到了这样一个场景:
1. 需要同时从多个Kafka实例里消费数以百万计的Topic
2. 每个Topic的写流量差异很大,有的几百MB/s,有的几B/min
3. 每个分区的写流量最大 5MB/s
原来的解决方案的:
1. 用多个消费服务实例
2. 每个实例上为每个Topic起一个Kafka消费者
这个方案的问题是:
1. Kafka连接数不足:一个消费服务实例上会为所有Topic创建消费者,随着消费服务实例数量的增加,kafka连接数将成倍增加
2. 数据倾斜问题:所有分区被随机分配到消费服务实例上,无法保证各个消费服务实例上所有分区的写流量相同;经常会发生,某个实例上分区的写流量过大,机器负载极高,消费堆积
这个方案的优点是:
1. 可靠性高:所有消费服务实例上都有每个Topic的消费者,挂掉几个实例,分区会被转移到其他消费者上,消费不会受到影响
2. 简单
2 分布式消费任务协调
2.1 消费服务启动
DB里有一个表,用来维护Topic的元数据,例如分区数。
消费服务启动时从DB里随机选一行,作为起点,为Topic创建消费者,创建后把消费者数据量写入到Redis里;如果消费者数量 > 分区数 1则不再创建消费者。
冗余两个消费者既保证了可靠性,又不至于让kafka连接数过多
2.2 消费服务实例负载过高时抛出任务
1. cpu、mem有一个过高,找一个流量中等的topic,抛到Redis上
2. 抛出前,负载要持续超限一段时间(5min),防止topic频繁移动
3. 轮询(10s )check抛到es上的topic是否被取走,若取走,close本机上的consumer
4. 如果一个高负载实例持续抛出topic失败,说明全局负载过高,则告警,收到告警后扩容
2.3 消费服务实例负载较低时领取任务
1. 从Redis上领取任务,开启消费者
2.4 兜底策略
1. 消费服务定时上报消费的Topic到Redis
2. 消费服务定时检查是否所有的Topic都被消费,对于没有消费的Topic启动消费