怎么做纯?

时间:2017-11-03 16:26:46

标签: scala akka-stream scala-cats

我有以下scala代码:

import akka.Done
import akka.actor.ActorSystem
import akka.kafka.ConsumerMessage.CommittableOffsetBatch
import akka.kafka.scaladsl.Consumer
import akka.kafka.{ConsumerSettings, Subscriptions}
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import org.apache.kafka.clients.consumer.{ConsumerConfig, ConsumerRecord}
import org.apache.kafka.common.serialization.StringDeserializer

import scala.concurrent.Future

object TestConsumer {
  def main(args: Array[String]): Unit = {

    implicit val system = ActorSystem("KafkaConsumer")
    implicit val materializer = ActorMaterializer()

    val consumerSettings = ConsumerSettings(system, new StringDeserializer, new StringDeserializer)
      .withBootstrapServers("localhost:9092")
      .withGroupId("group1")
      .withProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest")


    val result = Consumer
      .committableSource(consumerSettings, Subscriptions.topics("test"))
      .mapAsync(2)(rec => Future.successful(rec.record.value()))
      .runWith(Sink.foreach(ele => {
        print(ele)
        system.terminate()
      }))
  }
}

正如您所知,应用程序会消耗来自shell上打印的kafka的消息。

runWith不纯,会产生一些副作用,打印出收到的消息并关闭actor。

问题是,如何使用cats IO effects使其变得纯净?有可能吗?

1 个答案:

答案 0 :(得分:1)

你不需要猫IO来使它变得纯净。请注意,您的sink已经是纯粹的,因为它只是描述使用时会发生什么的价值(在这种情况下,使用意味着"连接到Source并运行流")。

    val sink: Sink[String, Future[Done]] = Sink.foreach(ele => {
      print(ele)
      // system.terminate() // PROBLEM: terminating the system before stream completes!
    })

您描述的问题与纯度无关。问题是上面的sink会关闭system的值,然后在处理源的每个元素时尝试terminate

终止system意味着您正在销毁用于运行流的整个运行时环境(由ActorMaterializer使用)。这应该只在您的流完成时完成。

val result: Future[Done] = Consumer
  .committableSource(consumerSettings, Subscriptions.topics("test"))
  .mapAsync(2)(rec => Future.successful(rec.record.value()))
  .runWith(sink)

result.onComplete(_ => system.terminate())