记录一下最近遇到的一个kafka的问题,kafka用的比较少,理解不深出现的问题,特此记录
问题描述业务要求: kafka上的消息要求顺序消费,且为了保证顺序在生产消息时也是顺序的投放到一个分区中.
一开始写的很简单,既然一个分区还要求顺序消费,那我就单线程消费呗,一个消费者消费一个分区单线程肯定是顺序的,刚开始也确实没有问题,但是运行一段时间后出现了问题.
接入生产环境数据后,kafka数据量突然上升一个量级,大概每天有接近1千万左右的数据,由于我们的消费者里还有一些业务逻辑,所以其实每条数据消费速度并不是很快,这样就导致了千万级的数据消费不过来,出现了消息堆积,经过1个月的消费堆积,kafak上大概堆积了有2百万的数据,极大的影响了业务正常运行.
优化过程然后就尝试了很多的优化方法,包括从业务/程序上优化,过滤一部分无效数据,但是效果并不明显.仍然有大量数据堆积.
后来经过一段事件的思考,突然有了一个新的想法: 二次投递
思路是这样的:
业务数据要求顺序,但是也可以将业务数据划分为N类,某一类的数据内保持顺序,这样的话我就启一个单线程消费者消费源端数据,在这个消费者内部将业务数据根据类型划分,投递到不同的分区,然后再根据分区数量启动N个消费者,每个消费者消费一个分区的数据,这样就把数据分散到多个消费者,且保证了数据的顺序性.
衍生问题
这样有衍生出了另外一个问题: 消费者太多,线程占用太多,服务器压力大
举例: 我现在有一千万数据,根据业务类型划分后会出现4个topic,每个topic的分区为10
这样我就需要40个消费者去消费分区数据,这样在开发时就需要开发40个消费者
解决办法后台查阅了kafka的一些资料后,发现可以使用一个消费者来消费多个分区数据,即消费者是可以指定分区的.
@KafkaListener(group="business-group",topicPartitions ={@TopicPartition(topic = "topic1", partitions = { "0", "1" }))}
可以指定partitions即消费分区数,支持多个分区
这样,我就可以针对某些分区,数量不大的情况下可以合并为同一个消费者消费,这样还是多个分区内的数据投递到一个消费者还是可以保证顺序的.
这里其实还有一个小问题: 不指定消费者的分区时,kafka是根据自己的规则来判断消费者的消费分区的,指定之后就被固定,做不到动态的消费了,可能造成某一个消费者压力较大的后果.
这里我又想到,是否可以通过一个程序来判断某个消费者是否空闲,空闲的话可以动态的指定压力较大的分区来消费,这个后续有时间再优化下
总结kafka分区就是为了提高读写速度,放弃了顺序性,只能在分区内部保证数据的顺序性,所以遇到顺序消费的数据,只能在分区内部消费处理.
但是为了提高消费速度,可以考虑上面这种方式,这种方式的缺点就是会多存储一份数据,并且要求数据从业务上可以划分.
思考: 有时候程序的优化并不是纯技术方面,可能需要结合业务才可以达到快速高效解决问题的目的,作为开发人员可能对业务也要有理解,需要有技术和业务相辅相成的思想.