web-dev-qa-db-ja.com

ソースに要素を動的に追加するにはどうすればよいですか?

バインドされていないソースを生成して操作するためのサンプルコードがあります。

オブジェクトメイン{

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

  implicit val system = ActorSystem("Sys")
  import system.dispatcher

  implicit val materializer = ActorFlowMaterializer()

  val source: Source[String] = Source(() => {
     Iterator.continually({ "message:" + ThreadLocalRandom.current().nextInt(10000)})
    })

  source.runForeach((item:String) => { println(item) })
  .onComplete{ _ => system.shutdown() }
 }

}

以下を実装するクラスを作成したい:

trait MySources {
    def addToSource(item: String)
    def getSource() : Source[String]
}

そして、私はそれを複数のスレッドで使用する必要があります、例えば:

class MyThread(mySources: MySources) extends Thread {
  override def run(): Unit = {
    for(i <- 1 to 1000000) { // here will be infinite loop
        mySources.addToSource(i.toString)
    }
  }
} 

そして期待される完全なコード:

object Main {
  def main(args : Array[String]): Unit = {
    implicit val system = ActorSystem("Sys")
    import system.dispatcher

    implicit val materializer = ActorFlowMaterializer()

    val sources = new MySourcesImplementation()

    for(i <- 1 to 100) {
      (new MyThread(sources)).start()
    }

    val source = sources.getSource()

    source.runForeach((item:String) => { println(item) })
    .onComplete{ _ => system.shutdown() }
  }
}

MySourcesを実装する方法は?

23
krynio

有限でないソースを作成する1つの方法は、ソースとして特別な種類のアクターを使用することです。これは、ActorPublisher特性を組み合わせたものです。これらの種類のアクターの1つを作成し、ActorPublisher.applyの呼び出しでラップすると、最終的にReactive Streams Publisherインスタンスが作成され、applyからSourceを使用してSourceを生成できます。その後、ActorPublisherクラスが要素をダウンストリームに送信するためのReactiveStreamsプロトコルを適切に処理することを確認する必要があります。これで、準備は完了です。非常に簡単な例は次のとおりです。

import akka.actor._
import akka.stream.actor._
import akka.stream.ActorFlowMaterializer
import akka.stream.scaladsl._

object DynamicSourceExample extends App{

  implicit val system = ActorSystem("test")
  implicit val materializer = ActorFlowMaterializer()

  val actorRef = system.actorOf(Props[ActorBasedSource])
  val pub = ActorPublisher[Int](actorRef)

  Source(pub).
    map(_ * 2).
    runWith(Sink.foreach(println))

  for(i <- 1 until 20){
    actorRef ! i.toString
    Thread.sleep(1000)
  }

}

class ActorBasedSource extends Actor with ActorPublisher[Int]{
  import ActorPublisherMessage._
  var items:List[Int] = List.empty

  def receive = {
    case s:String =>
      if (totalDemand == 0) 
        items = items :+ s.toInt
      else
        onNext(s.toInt)    

    case Request(demand) =>  
      if (demand > items.size){
        items foreach (onNext)
        items = List.empty
      }
      else{
        val (send, keep) = items.splitAt(demand.toInt)
        items = keep
        send foreach (onNext)
      }


    case other =>
      println(s"got other $other")
  }


}
20
cmbaxter
12
Loic

この回答 で述べたように、SourceQueueが進むべき道であり、Akka 2.5以降、便利な方法があります preMaterialize 最初に複合ソースを作成する必要がなくなります。

私の 他の答え で例を挙げます。

0
PetrosP