正确地以编程方式停止Alpakka Kafka流的方法

时间:2019-03-07 14:28:04

标签: scala apache-kafka akka akka-stream alpakka

我们正尝试将Akka Streams与Alpakka Kafka结合使用以使用服务中的事件流。为了处理事件处理错误,我们使用Kafka自动提交和多个队列。例如,如果我们有一个主题user_created,我们想从产品服务中使用它,那么我们还将创建user_created_for_products_faileduser_created_for_products_dead_letter。这两个额外的主题与特定的Kafka消费者群体相关。如果事件无法处理,它将进入失败的队列,我们​​将在五分钟内尝试再次消耗事件;如果事件再次失败,则变为死信。

在部署时,我们要确保我们不会丢失事件。因此,我们试图在停止应用程序之前停止流。就像我说的,我们正在使用自动提交,但是所有这些正在“飞行”的事件尚未得到处理。流和应用程序停止后,我们可以部署新代码并重新启动应用程序。

阅读文档后,我们看到了KillSwitch功能。我们在其中看到的问题是shutdown方法返回Unit而不是我们期望的Future[Unit]。我们不确定使用它不会丢失事件,因为在测试中,它看起来太快而无法正常工作。

作为一种解决方法,我们为每个流创建一个ActorSystem,并使用terminate方法(该方法返回一个Future[Terminate])。该解决方案的问题在于,我们认为为每个流创建一个ActorSystem不能很好地扩展,并且terminate需要花费很多时间来解决(在测试中,关闭过程最多需要一分钟下)。

您遇到过这样的问题吗?是否有更快的方法(与ActorSystem.terminate相比)停止流并确保Source发出的所有事件都已处理?

1 个答案:

答案 0 :(得分:4)

来自documentation(重点是我):

  

使用外部偏移量存储时,对Consumer.Control.shutdown()的调用足以完成Source,从而开始完成流。

val (consumerControl, streamComplete) =
  Consumer
    .plainSource(consumerSettings,
                 Subscriptions.assignmentWithOffset(
                   new TopicPartition(topic, 0) -> offset
                 ))
    .via(businessFlow)
    .toMat(Sink.ignore)(Keep.both)
    .run()

consumerControl.shutdown()

Consumer.control.shutdown()返回一个Future[Done]。从其Scaladoc描述:

  

关闭使用者Source。在关闭之前,它将等待未完成的偏移提交请求完成。

或者,如果您在Kafka中使用偏移量存储,请使用Consumer.Control.drainAndShutdown,它还会返回Future。再次从文档中获得(该文档包含有关drainAndShutdown的内容的更多信息):

val drainingControl =
  Consumer
    .committableSource(consumerSettings.withStopTimeout(Duration.Zero), Subscriptions.topics(topic))
    .mapAsync(1) { msg =>
      business(msg.record).map(_ => msg.committableOffset)
    }
    .toMat(Committer.sink(committerSettings))(Keep.both)
    .mapMaterializedValue(DrainingControl.apply)
    .run()

val streamComplete = drainingControl.drainAndShutdown()

drainAndShutdown的Scaladoc描述:

  

停止从Source产生消息,等待流完成并关闭使用者Source,以便所有消耗的消息到达流的末尾。流完成失败将被传播,无论如何将关闭源。