web-dev-qa-db-ja.com

後でメソッド呼び出しを介して要素を受け取ることができるソースを作成する方法は?

次のように、Sourceと後でPush要素を作成したいと思います。

val src = ... // create the Source here
// and then, do something like this
pushElement(x1, src)
pushElement(x2, src)

これを行うための推奨される方法は何ですか?

ありがとう!

50
ale64bit

これを実現するには、次の3つの方法があります。

1。 SourceQueueを使用した実体化後

フローをSourceQueueに具体化するSource.queueを使用できます。

case class Weather(zipCode : String, temperature : Double, raining : Boolean)

val bufferSize = 100

//if the buffer fills up then this strategy drops the oldest elements
//upon the arrival of a new element.
val overflowStrategy = akka.stream.OverflowStrategy.dropHead

val queue = Source.queue(bufferSize, overflowStrategy)
                  .filter(!_.raining)
                  .to(Sink foreach println)
                  .run() // in order to "keep" the queue Materialized value instead of the Sink's

queue offer Weather("02139", 32.0, true)

2。アクターを使用した実体化後

同様の質問と回答があります ここ 、要点は、ストリームをActorRefとして具体化し、そのrefにメッセージを送信することです:

val ref = Source.actorRef[Weather](Int.MaxValue, fail)
                .filter(!_.raining)
                .to(Sink foreach println )
                .run() // in order to "keep" the ref Materialized value instead of the Sink's

ref ! Weather("02139", 32.0, true)

3。アクターによる事前実体化

同様に、メッセージバッファを含むアクターを明示的に作成し、そのアクターを使用してSourceを作成し、回答で説明されているようにそのアクターメッセージを送信できます here

object WeatherForwarder {
  def props : Props = Props[WeatherForwarder]
}

//see provided link for example definition
class WeatherForwarder extends Actor {...}

val actorRef = actorSystem actorOf WeatherForwarder.props 

//note the stream has not been instatiated yet
actorRef ! Weather("02139", 32.0, true) 

//stream already has 1 Weather value to process which is sitting in the 
//ActorRef's internal buffer
val stream = Source(ActorPublisher[Weather](actorRef)).runWith{...}

いろいろと遊んでこれに対する良い解決策を探した後、私はこの解決策に出くわしました。これはクリーンでシンプルで、実体化の前後に機能します。 https://stackoverflow.com/a/32553913/6791842

  val (ref: ActorRef, publisher: Publisher[Int]) =
    Source.actorRef[Int](bufferSize = 1000, OverflowStrategy.fail)
      .toMat(Sink.asPublisher(true))(Keep.both).run()

  ref ! 1 //before

  val source = Source.fromPublisher(publisher)

  ref ! 2 //before
  Thread.sleep(1000)
  ref ! 3 //before

  source.runForeach(println)

  ref ! 4 //after
  Thread.sleep(1000)
  ref ! 5 //after

出力:

1
2
3
4
5
3
Jesse Feinman

Akka 2.5以降、Sourceには preMaterialize メソッドがあります。

documentation によると、これはあなたが求めることを行うための指示された方法のように見えます:

Sourceがグラフの残りの部分に接続される前に、Sourceの実体化された値が必要な場合があります。これは、Source.queueSource.actorRef、またはSource.maybeなどの「マテリアライズドバリューパワード」ソースの場合に特に便利です。

SourceQueueを使用した場合の例を以下に示します。要素は、Flow内からだけでなく、実体化の前後にキューにプッシュされます。

import akka.actor.ActorSystem
import akka.stream.scaladsl._
import akka.stream.{ActorMaterializer, OverflowStrategy}

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


val sourceDecl = Source.queue[String](bufferSize = 2, OverflowStrategy.backpressure)
val (sourceMat, source) = sourceDecl.preMaterialize()

// Adding element before actual materialization
sourceMat.offer("pre materialization element")

val flow = Flow[String].map { e =>
  if(!e.contains("new")) {
    // Adding elements from within the flow
    sourceMat.offer("new element generated inside the flow")
  }
  s"Processing $e"
}

// Actually materializing with `run`
source.via(flow).to(Sink.foreach(println)).run()

// Adding element after materialization
sourceMat.offer("post materialization element")

出力:

Processing pre materialization element
Processing post materialization element
Processing new element generated inside the flow
Processing new element generated inside the flow
1
PetrosP