背景介绍
在调用sarama时,kafka对消费组进行消费均通过实现 ConsumerGroupHandler 来对消息进行处理,在 ConsumerGroupHandler接口中,包含了三个方法,其中ConsumeClaim(ConsumerGroupSession, ConsumerGroupClaim) error 会循环消费 ConsumerGroupClaim.Messages() 中的消息,并在通道关闭时退出循环。
其他两个方法则是新会话开始时的运行的 Setup() 和所有监听函数协程退出时运行的CleanUp() 。
提出问题
正如前文所说,sarama如果想要消费,必定是流式的从Messages返回的通道中读取数据,因此并不适合批量消费,但我们的需求很可能需要我们对kafka的消息进行批量消费,因此,在流式消费的基础上,我们需要将消息存入某个batch中,并分批进行消费。
具体实现
msgs := make(string, 0, BATCHSIZE)
for msg in claim.Messages() {
msgs = append(msgs, msg)
if len(msgs) >= BATCHSIZE {
// 消费消息
}
}
如果这么实现,则会产生一个问题,如果生产者只生产了BATCHSIZE - 1的消息,那么就永远不会开始消费,而且无论生产者生产了多少消息,均会产生一个未消费的尾巴,除非生产者生产的消息恰好可以被BATCHSIZE整除。这个尾巴会带来两个问题
- 尾巴永远不会被消费
- 如果中途突然中断,这些尾巴数据就会被丢失
前者可以通过超时时间判断来进行去尾,而后者则必须设置手动提交offset来替代自动提交offset。
msgs := make(string, 0, BATCHSIZE)
for {
select {
case msg := <- claims.Messages():
// 添加至数组
case <- time.After(time.Second * 1):// 一秒后超时
// 消费整个数组
}
}
这里需要注意下,手动提交offset的节点有两个,也就是消费数组的两个节点,即在流式消费时数组达到BATCHSIZE时对整个数组进行消费和在超时时对整个尾巴数组进行消费两个节点。
忘了什么?
再回忆一下尾巴带来的第二个问题:
如果中途突然中断,这些尾巴数据就会被丢失
现在在消息中断的时候,尾巴数据还未提交对应的offset,自然不会丢失,但是外层的 claim.Messages() 并不会因此重新读到这些尾巴数据,因此除非我们重启整个服务,否则依然无法再次消费这些尾巴数据。
因此我们需要在中断时退出整个监听程序,并在之后重新建立回话时选择最老的offset
这需要在建立kafkaClient时配置对应的config实现。