实施kafka消费者时应该采用哪种更好的方法。
目标是从Kafka读取并写回db。数百万行
方法1: 每个分区 - 每个消费者 - 等待消息消耗(即写回数据库)然后在轮询循环中继续下一步。
方法2: 每个分区 - 每个消费者 - 将记录发送到工作线程或线程池以写回数据库,稍后提交偏移量并继续轮询。需要注意抵消管理。在此不要等待消息写回DB。继续轮询,将消息传递给工作线程。
关于他们两个的任何见解?
由于
答案 0 :(得分:3)
方法1: 仅当您可以估计消息处理时间时,该方法才适用,否则不建议使用。
问题:在这种方法中,主要问题是让消费者保持活力,如果您在再次调用poll()之前等待邮件被完全处理,您必须确保消费者应该活着,直到它调用poll(),因为kafka维护一个名为" session.timeout.ms"的属性。 kafka代理/集群对此属性的值采取操作,如果消费者无法在" session.timeout.ms"的时间段内再次调用poll(),则代理将标记消费者死亡和它会被踢出去。现在,当消费者完成消息处理并再次调用poll()时,它将被视为一个新的连接器,并将再次从偏移量开始给出一组记录。记住这种情况,消费者将陷入无限循环,永远不会进行抵消。
可能的解决方案1:要使用此方法,您需要具有以下属性的良好价值" session.timeout.ms"有以下副作用:
1:值太低:消费者将被标记为死亡,如上所述并且永远不会继续其偏移,但是消息将被处理,但每次完成消息时它将再次获得先前消息+新消息。
2:价值太高:经纪人在检测消费者的真正失败时会很晚,这会导致记录重复并影响整体吞吐量。
可能的解决方案2 :(仅对版本0.10.1.x有效) Kafka在发布时的正式修复(0.10.1.0)。 在这种方法中,引入了两个值得注意的实体:一个新属性" max.poll.interval.ms"设置客户端调用poll()和后台线程之间的最大延迟,后者负责使消费者保持活动状态。因此,在一个场景中,当消费者调用方法poll()然后忙于消息处理时,内部后台线程将保持心跳活跃,因此消费者将保持活力。但是,此内部后台线程本身将保持活动状态,直到属性“max.poll.interval.ms”的超时值仍然有效。因此,该线程将等待消费者在时间段值“max.poll.interval.ms”中调用poll(),否则,它将发送请假请求并且也将自行消亡。"
此解决方案中棘手的部分是找到此属性的合适值:" max.poll.interval.ms" (非常重要,这个时间将是后台线程在不需要显式调用poll()的情况下保持心跳活动的时间。)
方法2:使用工作线程是一个好主意,但是你必须维护一个内部队列或验证收到的消息,这些消息可能很复杂,你还需要对自动提交使用手动提交。有关提交的详细信息,请参阅this和搜索标题"提交和抵消"。
问题:在这种方法中,主要问题是跟踪收到的消息和成功处理的消息。因为,您的消费者将收到消息,它将消息传递给相应的工作线程,并将提交偏移量并继续前进以接收更多消息。在此过程中,您必须处理以下问题:
解决方案:可以有不同的方法来解决上述问题,一种方法是使用内部队列来保留仅在工作线程报告成功时才会发送的消息和手动提交处理消息。但是,需要非常仔细的实现,因为它可能导致复杂的代码,并且还可能导致内存管理或线程问题。
建议:根据您的要求,您可以使用一种方法或另一种方法,针对上述可能的问题实施已修复的方法。但是我建议使用更强大的解决方案来使用分区暂停/恢复。以非常抽象的方式,您的消费者应该执行以下步骤:
1:对消息进行poll()。
2:暂停所有相应的主题/分区。
3:为工作线程分配消息并等待其处理。
4:继续调用poll()但是当分区暂停时,消费者将保持活动状态时不会收到额外的消息。 (确保在此期间没有注册新主题)
5:如果所有工作线程都应报告消息处理成功/失败,则相应地提交偏移量。
6:恢复所有分区。
注意:根据您的方案和要求,可以有更好的方法或其他解决方案。它只是一个想法或可能的解决方案之一。