如何使用Akka流

时间:2018-01-02 08:23:42

标签: scala akka akka-stream

我正在学习Akka并尝试了解GraphStage的工作,但我正在努力解决物化价值的使用问题。

我想帮助解决以下问题:

  

如何将第一流的物化价值消耗到第二流?

请提供指示,如果有的话

由于

1 个答案:

答案 0 :(得分:2)

我花了一些时间来思考你的问题。简而言之,我的回答是:根据我对akka-stream的理解,你的问题毫无意义。以下是一个扩展说明。此外,我的知识是有限的,所以如果我假设不正确 - 请评论,让我们一起学习。

在考虑我们如何消耗Flow的具体化价值之前,我们首先需要一个Flow的实例值与NotUsed不同的实例值 - 一个在大多数情况下使用的值 - 盒子流动。 所以,我编写了一个简单的Flow,它只是通过所有上游元素,但是有一个嵌入的计数器,用于计算传递的总元素数。 这是代码:

class FlowWithCounter extends GraphStageWithMaterializedValue[FlowShape[Int, Int], Future[Int]]{
    val in:Inlet[Int] = Inlet("Inlet_of_FlowWithCounter")
    val out:Outlet[Int] = Outlet("Outlet of FlowWithCounter")

    override def shape: FlowShape[Int, Int] = FlowShape(in, out)

    override def createLogicAndMaterializedValue(inheritedAttributes: Attributes): (GraphStageLogic, Future[Int]) = {
      val materializedValue = Promise[Int]()
      val logic = new GraphStageLogic(shape) {
        private var counter: Int = 0

        setHandler(
          in,
          new InHandler {
            override def onPush(): Unit = {
              val elem = grab(in)
              counter += 1
              push(out, elem)
            }

            override def onUpstreamFinish(): Unit = {
              materializedValue.success(counter)
              super.onUpstreamFinish()
            }
          }
        )

        setHandler(out, new OutHandler {
          override def onPull(): Unit = {
            pull(in)
          }
        })
      }
      (logic, materializedValue.future)
    }
  }

如您所见,物化值类型为Future [Int]。因此,当流完成时,物料化器将能够获取此值并在我们指示它时使用它。

现在很少有重要的观察结果:

  1. 在我们运行流时,createLogicAndMaterializedValue方法将不会被流Materializer调用。所以我们应该在那一刻返回一些指针,它将在流完成时刻保持物化值。这通常以Future[T]自然形式完成。
  2. 如上所述here

      

    //所有状态必须在GraphStageLogic中,

         

    //永远不会在封闭的GraphStage中。

         

    //从所有

    访问和修改此状态是安全的      

    //由GraphStageLogic和

    提供的回调      

    //注册处理程序。

    在我们的示例中,这意味着我们只能访问counter构造函数中的new GraphStageLogic(shape) {...}变量。我们无法在返回counter对的行中引用(logic, materializedValue.future)本身。

  3. 我没有在DSL中找到任何查询流组件的具体化值的方法。只有借助toMatviaMat等方法,我们才能告诉物化器传播下游组件的具体化值。只有在run等方法的帮助下,我们才能最终提取整个流的最终物化价值。

    注意到我们的示例流程使用

    中的counter值完成了承诺
    override def onUpstreamFinish(): Unit = {
      materializedValue.success(counter)
      super.onUpstreamFinish()
    }
    

    上游完成时。

    以下是我们实际连接此样本流的方式,通过流阶段传播其物化值并在整个流完成时最终使用它:

    import akka.stream.stage._
    
    object Main {
      import scala.concurrent._
      import akka._
      import akka.actor._
      import akka.stream._
      import akka.stream.scaladsl._
    
      implicit val system = ActorSystem("TestSystem")
      implicit val materializer = ActorMaterializer()
      import system.dispatcher
    
      class FlowWithCounter extends GraphStageWithMaterializedValue[FlowShape[Int, Int], Future[Int]]{....}
    
      def main(args:Array[String]):Unit = {
        val source:Source[Int, NotUsed] = Source(15 to 25)
        val flow:Flow[Int, Int, Future[Int]] = Flow.fromGraph(new FlowWithCounter)
        val sink:Sink[Int, Future[Done]] = Sink.foreach(println)
    
        val completion:(Future[Int], Future[Done]) = ((source.viaMat(flow)(Keep.right)).toMat(sink)(Keep.both)).run()
        completion._2 foreach{_ =>
          completion._1 foreach{count =>
            println("Our flow processed " + count + " elements.")
            system.terminate()
          }
        }
      }
    
    }
    

    因此,我们流程的具体化价值仅在阶段完成时可用。 Flow组件与上游源和下游接收器的具体化值无关。据我所知,只有物化器和流解释器才能获取这些物化值并适当地传播它们。

    也许你的问题对于无限流有一些意义,我可以想象第二流需要在某种逻辑条件下检查第一流中嵌入的计数器的情况 - 但我不知道如何实现这一点。也许有人会在其他答案中解释。