=str #16549 doc: ActorPublisher
This commit is contained in:
parent
61c55ae0e3
commit
4dcb0d9834
4 changed files with 275 additions and 10 deletions
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* Copyright (C) 2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.stream
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import akka.actor.Props
|
||||
import akka.stream.FlowMaterializer
|
||||
import akka.stream.actor.ActorPublisher
|
||||
import akka.stream.scaladsl.Sink
|
||||
import akka.stream.scaladsl.Source
|
||||
import akka.stream.testkit.AkkaSpec
|
||||
|
||||
object ActorPublisherDocSpec {
|
||||
|
||||
//#job-manager
|
||||
object JobManager {
|
||||
def props: Props = Props[JobManager]
|
||||
|
||||
final case class Job(payload: String)
|
||||
case object JobAccepted
|
||||
case object JobDenied
|
||||
}
|
||||
|
||||
class JobManager extends ActorPublisher[JobManager.Job] {
|
||||
import akka.stream.actor.ActorPublisherMessage._
|
||||
import JobManager._
|
||||
|
||||
val MaxBufferSize = 100
|
||||
var buf = Vector.empty[Job]
|
||||
|
||||
def receive = {
|
||||
case job: Job if buf.size == MaxBufferSize =>
|
||||
sender() ! JobDenied
|
||||
case job: Job =>
|
||||
sender() ! JobAccepted
|
||||
if (buf.isEmpty && totalDemand > 0)
|
||||
onNext(job)
|
||||
else {
|
||||
buf :+= job
|
||||
deliverBuf()
|
||||
}
|
||||
case Request(_) =>
|
||||
deliverBuf()
|
||||
case Cancel =>
|
||||
context.stop(self)
|
||||
}
|
||||
|
||||
@tailrec final def deliverBuf(): Unit =
|
||||
if (totalDemand > 0) {
|
||||
if (totalDemand <= Int.MaxValue) {
|
||||
val (use, keep) = buf.splitAt(totalDemand.toInt)
|
||||
buf = keep
|
||||
use foreach onNext
|
||||
} else {
|
||||
val (use, keep) = buf.splitAt(Int.MaxValue)
|
||||
buf = keep
|
||||
use foreach onNext
|
||||
deliverBuf()
|
||||
}
|
||||
}
|
||||
}
|
||||
//#job-manager
|
||||
}
|
||||
|
||||
class ActorPublisherDocSpec extends AkkaSpec {
|
||||
import ActorPublisherDocSpec._
|
||||
|
||||
implicit val mat = FlowMaterializer()
|
||||
|
||||
"illustrate usage of ActorPublisher" in {
|
||||
def println(s: String): Unit =
|
||||
testActor ! s
|
||||
|
||||
//#actor-publisher-usage
|
||||
val jobManagerSource = Source[JobManager.Job](JobManager.props)
|
||||
val materializedMap = jobManagerSource
|
||||
.map(_.payload.toUpperCase)
|
||||
.map { elem => println(elem); elem }
|
||||
.to(Sink.ignore)
|
||||
.run()
|
||||
|
||||
val ref = materializedMap.get(jobManagerSource)
|
||||
ref ! JobManager.Job("a")
|
||||
ref ! JobManager.Job("b")
|
||||
ref ! JobManager.Job("c")
|
||||
//#actor-publisher-usage
|
||||
|
||||
expectMsg("A")
|
||||
expectMsg("B")
|
||||
expectMsg("C")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* Copyright (C) 2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.stream
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.Props
|
||||
import akka.routing.ActorRefRoutee
|
||||
import akka.routing.RoundRobinRoutingLogic
|
||||
import akka.routing.Router
|
||||
import akka.stream.FlowMaterializer
|
||||
import akka.stream.actor.ActorSubscriber
|
||||
import akka.stream.actor.ActorSubscriberMessage
|
||||
import akka.stream.actor.MaxInFlightRequestStrategy
|
||||
import akka.stream.scaladsl.Sink
|
||||
import akka.stream.scaladsl.Source
|
||||
import akka.stream.testkit.AkkaSpec
|
||||
|
||||
object ActorSubscriberDocSpec {
|
||||
//#worker-pool
|
||||
object WorkerPool {
|
||||
case class Msg(id: Int, replyTo: ActorRef)
|
||||
case class Work(id: Int)
|
||||
case class Reply(id: Int)
|
||||
case class Done(id: Int)
|
||||
|
||||
def props: Props = Props(new WorkerPool)
|
||||
}
|
||||
|
||||
class WorkerPool extends ActorSubscriber {
|
||||
import WorkerPool._
|
||||
import ActorSubscriberMessage._
|
||||
|
||||
val MaxQueueSize = 10
|
||||
var queue = Map.empty[Int, ActorRef]
|
||||
|
||||
val router = {
|
||||
val routees = Vector.fill(3) {
|
||||
ActorRefRoutee(context.actorOf(Props[Worker]))
|
||||
}
|
||||
Router(RoundRobinRoutingLogic(), routees)
|
||||
}
|
||||
|
||||
override val requestStrategy = new MaxInFlightRequestStrategy(max = MaxQueueSize) {
|
||||
override def inFlightInternally: Int = queue.size
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case OnNext(Msg(id, replyTo)) ⇒
|
||||
queue += (id -> replyTo)
|
||||
assert(queue.size <= MaxQueueSize, s"queued too many: ${queue.size}")
|
||||
router.route(Work(id), self)
|
||||
case Reply(id) ⇒
|
||||
queue(id) ! Done(id)
|
||||
queue -= id
|
||||
}
|
||||
}
|
||||
|
||||
class Worker extends Actor {
|
||||
import WorkerPool._
|
||||
def receive = {
|
||||
case Work(id) ⇒
|
||||
// ...
|
||||
sender() ! Reply(id)
|
||||
}
|
||||
}
|
||||
//#worker-pool
|
||||
|
||||
}
|
||||
|
||||
class ActorSubscriberDocSpec extends AkkaSpec {
|
||||
import ActorSubscriberDocSpec._
|
||||
|
||||
implicit val mat = FlowMaterializer()
|
||||
|
||||
"illustrate usage of ActorSubscriber" in {
|
||||
val replyTo = testActor
|
||||
|
||||
//#actor-subscriber-usage
|
||||
val N = 117
|
||||
Source(1 to N).map(WorkerPool.Msg(_, replyTo))
|
||||
.runWith(Sink(WorkerPool.props))
|
||||
//#actor-subscriber-usage
|
||||
|
||||
receiveN(N).toSet should be((1 to N).map(WorkerPool.Done).toSet)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue