Akka Stream Kafka,当到达日志结束时完整的流

时间:2018-04-03 14:21:33

标签: scala apache-kafka akka reactive-kafka

我正在使用Akka Streams Kafka,我正在寻找一种方法来执行以下操作:

  • 来自偏移x
  • 的启动流
  • 按顺序使用项目xx+1x+2 ..直到最后一项
  • 消耗完最后一项后,完成流

代码看起来像

Consumer
  .plainSource(consumerSettings, subscription)
  .runForeach(println("got record!"))
  .onComplete {
    case Success(_) => // all items read
    case Failure(error) => // error
  }

它将在读完最后一个元素后完成。也许这不是这个库的使用方式。我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:3)

Akka Consumer以“拉动”的方式工作,除非与经纪人发生错误,否则它将永远存在。但是,你什么时候考虑流已经结束? Kafka可以被视为分布式日志,您可以从中读取给定偏移量的消息。只要您的客户端连接到Broker,您的客户端就会启动并运行...如果您考虑在Kafka没有事件发生一段时间间隔时(例如)您的流终止,则可以使用 idleTimeout

  Consumer
    .plainSource(consumerSettings, subscription)
    .idleTimeout(10 seconds)
    .runForeach(e => println("E"))
    .onComplete {
      case Success(_) => // all items read
      case Failure(error) =>
      // TimeoutException if no element in ten seconds the stream stops throwing this exception
    }

另一种可能性是使用Fan-In阶段,特别是 MergePreferred 。我们可以创建另一个Tick Source,它在一个时间间隔内发出事件。 Kafka来源会有偏好,所以就Kafka的元素来说,舞台总会从这个来源中拉出元素。如果某个区间中没有元素,则会向下游推送“超时”字符串。类似的东西:

  implicit val actorSystem = ActorSystem("test-actor-system")
  implicit val streamMaterializer = ActorMaterializer()
  implicit val ec = actorSystem.dispatcher

  val consumer =
  Consumer
    .plainSource(consumerSettings, subscription)
    .map(_.value())

  val tick = Source.tick(50 millis, 30 seconds, "Timeout")

  val source = GraphDSL.create(consumer, tick)(Keep.both) { implicit b ⇒
    (r1, r2) ⇒
      val merge = b.add(MergePreferred[String](1, false))
      r2 ~> merge.in(0)
      r1 ~> merge.preferred
      SourceShape(merge.out)
  }

  Source
    .fromGraph(source)
    .takeWhile(el => el != "Timeout")
    .runForeach(msg => println(msg))
  .onComplete{
    case Success(_) => println("Stream ended")
    case Failure(error) => println("There was an error")
  }

使用takeWhile,流将处于活动状态,同时有来自Kafka的元素。

这只是一种方法。 Akka Stream有许多不同的阶段,Graph Api可能以更优雅的方式面对这些情况。