The following actor performs a division once both numerator and denominator are received,
package funnelTest
import akka.actor.{Actor, ActorSystem, Props}
object Main extends App {
val system1 = ActorSystem("funnelTest")
val input = system1.actorOf(Props[Funnel], "input")
input ! 3
input ! 2.718
}
case object Run
class Funnel extends Actor {
var i: Option[Int] = None
var d: Option[Double] = None
def isReady = i.nonEmpty && d.nonEmpty
def receive = {
case v: Int => i = Some(v) ; if (isReady) self ! Run
case v: Double => d = Some(v) ; if (isReady) self ! Run
case Run => println(s"aggregated, $d / $i = " + d.get/i.get)
case _ =>
}
}
Is there a more scalable way to aggregate all the messages?
A unique identifier that identifies the request is one way to solve the problem. A map ( calcRegistry
) inside the Actor holds the previously arrived FractionComponent
(either Numerator
or Denominator
). Once the second part of the pair comes in, we could start running the calculation as you've already done.
The implementation still doesn't address the issue of a memory leak where the second pair wouldn't be received and the map would keep growing.
import akka.actor.{Actor, ActorSystem, Props}
object Main extends App {
import Funnel._
val system1 = ActorSystem("funnelTest")
val input = system1.actorOf(Props[Funnel], "input")
(1 to 10) foreach { number =>
val id = java.util.UUID.randomUUID().toString
input ! Numerator(id, value = number + 2)
input ! Denominator(id, value = number + 1)
}
system1.awaitTermination()
}
class Funnel extends Actor {
import Funnel._
import scala.collection._
val calcRegistry = mutable.Map[String, FractionComponent]()
def saveToRegistry(comp: FractionComponent) = calcRegistry(comp.id) = comp
def printValue(num: Numerator, den: Denominator) = println(s"aggregated, ${num.value} / ${den.value} = ${num.value / den.value}")
def receive = {
case num@Numerator(id, _) =>
if (calcRegistry contains id)
self ! Run(num, calcRegistry(id).asInstanceOf[Denominator])
else saveToRegistry(num)
case den@Denominator(id, _) =>
if (calcRegistry contains id)
self ! Run(calcRegistry(id).asInstanceOf[Numerator], den)
else saveToRegistry(den)
case Run(num: Numerator, den: Denominator) =>
calcRegistry.remove(num.id)
printValue(num, den)
case _ =>
}
}
object Funnel {
sealed trait FractionComponent {
def id: String
}
case class Numerator(override val id: String, value: Double) extends FractionComponent
case class Denominator(override val id: String, value: Integer) extends FractionComponent
case class Run(num: Numerator, denominator: Denominator)
}
Sample output :
aggregated, 3.0 / 2 = 1.5
aggregated, 4.0 / 3 = 1.3333333333333333
aggregated, 5.0 / 4 = 1.25
aggregated, 6.0 / 5 = 1.2
aggregated, 7.0 / 6 = 1.1666666666666667
aggregated, 8.0 / 7 = 1.1428571428571428
aggregated, 9.0 / 8 = 1.125
aggregated, 10.0 / 9 = 1.1111111111111112
aggregated, 11.0 / 10 = 1.1
aggregated, 12.0 / 11 = 1.0909090909090908
Reference : Reactive Messaging Patterns with the Actor Model
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.