Akka(2)Start with first Project - First Part
Use actor to calculate the value of Pi.
Create the project
Install the Typesafe akka-scala-sbt project template first.
Try to get the scala akka project template:
>g8 typesafehub/akka-scala-sbt
Akka 2.0 Project Using Scala and sbt
organization [org.example]: com.sillycat.easyakka
name [Akka Project In Scala]: easyakka
akka_version [2.0.1]:
version [0.1-SNAPSHOT]:
Applied typesafehub/akka-scala-sbt.g8 in easyakka
Try to get the scala akka demo source code:
>g8 typesafehub/akka-first-tutorial-scala
Akka 2.0 project using Scala and sbt
organization [org.example]: com.sillycat.easyakka
package [org.example]: com.sillycat.easyakka
name [Akka Pi Calculation Tutorial In Scala]: easyakka
akka_version [2.0.3]:
version [0.1-SNAPSHOT]:
artifact_id [akka-pi-scala]: easyakka
Skipping existing file: easyakka/README
Applied typesafehub/akka-first-tutorial-scala.g8 in easyakka
>cd easyakka
>sbt update
>sbt eclipse
Import the project to my eclipse.
Run the project with simple build tool
>sbt compile
>sbt run
Everything works fine here.
Run it from eclipse:
I just open the file Pi.scala and right click on that and run scala application.
Error Message:
Exception in thread "main" java.lang.NoSuchMethodError: scala.Predef$.augmentString(Ljava/lang/String;)Lscala/collection/immutable/StringOps;
Solution:
That is mostly related to using different versions of Scala.
I change my low version of eclipse and scala to 2.9.2. That works fine. I do not want to speed time to solve the version problem.
I just want to understand how the actor works in akka. So let's move on.
Try to understand the Design and Implementation
Import the package:
import akka.actor._
import akka.routing.RoundRobinRouter
import akka.util.Duration
import akka.util.duration._
Design
Master ----> Worker(N)
<----
Master ----> Listener
Messages:
Calculate - sent to the Master actor to start the calculation
Work - send from the Master actor to the Worker actors
Result - send from the Worker actors to the Master actor
PiApproximation - send from the Master actor to the Listener actor
Messages sent to actors should always be immutable to avoid sharing mutable state. 'case class' make excellent messages.
sealed trait PiMessage
case object Calculate extends PiMessage
case class Work(start: Int, nrOfElements: Int) extends PiMessage
case class Result(value: Double) extends PiMessage
case class PiApproximation(pi: Double, duration: Duration)
A sealed trait can be extended only in the same file than its declaration.
Creating the worker
class Worker extends Actor {
//get the start and number
def calculatePiFor(start: Int, nrOfElements: Int): Double = {
var acc = 0.0
for (i <- start until (start + nrOfElements))
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1)
acc //return the latest line
}
//receive the task data from message, sender back once we are done
def receive = {
case Work(start, nrOfElements) =>
sender ! Result(calculatePiFor(start, nrOfElements)) // perform the work
}
}
We send the message back to the sender using the sender reference.
In Akka the sender reference is implicitly passed along with the message so that the receiver can always reply or store away the sender reference for future use.
Creating the master
The master actor is a little bit more involved.
class Master(nrOfWorkers: Int,
nrOfMessages: Int,
nrOfElements: Int,
listener: ActorRef) extends Actor { // listener is a ActorRef we can send message
var pi: Double = _
var nrOfResults: Int = _
val start: Long = System.currentTimeMillis //get the start time
//define the number of workers and using round-robin router
val workerRouter = context.actorOf(
Props[Worker].withRouter(RoundRobinRouter(nrOfWorkers)), name = "workerRouter")
def receive = {
case Calculate =>
for (i <- 0 until nrOfMessages)
workerRouter ! Work(i * nrOfElements, nrOfElements) // divide the tasks, send all tasks to workerRouter
case Result(value) => // case it is the sender message about result from the worker
pi += value
nrOfResults += 1
if (nrOfResults == nrOfMessages) { //get all the messages back
listener ! PiApproximation(pi, duration = (System.currentTimeMillis - start).millis)
context.stop(self) // stop the master actor
}
}
}
Our master have 3 parameters.
nrOfWorkers how many workers we should start up
nrOfMessages how many chunks to send out to the workers
nrOfElements how big the number chunks send to each worker
The ActorRef is used to report the final result to the outside world.
References:
http://doc.akka.io/docs/akka/2.1.0/
http://doc.akka.io/docs/akka/2.1.0/general/index.html
https://github.com/typesafehub
http://typesafe.com/resources/tutorials/getting-started-with-akka-scala.html#getting-started-with-akka-scala
http://alvinalexander.com/scala/java.lang.nosuchmethoderror-scala.predef.augmentstring-error
http://stackoverflow.com/questions/11203268/what-is-a-sealed-trait
Use actor to calculate the value of Pi.
Create the project
Install the Typesafe akka-scala-sbt project template first.
Try to get the scala akka project template:
>g8 typesafehub/akka-scala-sbt
Akka 2.0 Project Using Scala and sbt
organization [org.example]: com.sillycat.easyakka
name [Akka Project In Scala]: easyakka
akka_version [2.0.1]:
version [0.1-SNAPSHOT]:
Applied typesafehub/akka-scala-sbt.g8 in easyakka
Try to get the scala akka demo source code:
>g8 typesafehub/akka-first-tutorial-scala
Akka 2.0 project using Scala and sbt
organization [org.example]: com.sillycat.easyakka
package [org.example]: com.sillycat.easyakka
name [Akka Pi Calculation Tutorial In Scala]: easyakka
akka_version [2.0.3]:
version [0.1-SNAPSHOT]:
artifact_id [akka-pi-scala]: easyakka
Skipping existing file: easyakka/README
Applied typesafehub/akka-first-tutorial-scala.g8 in easyakka
>cd easyakka
>sbt update
>sbt eclipse
Import the project to my eclipse.
Run the project with simple build tool
>sbt compile
>sbt run
Everything works fine here.
Run it from eclipse:
I just open the file Pi.scala and right click on that and run scala application.
Error Message:
Exception in thread "main" java.lang.NoSuchMethodError: scala.Predef$.augmentString(Ljava/lang/String;)Lscala/collection/immutable/StringOps;
Solution:
That is mostly related to using different versions of Scala.
I change my low version of eclipse and scala to 2.9.2. That works fine. I do not want to speed time to solve the version problem.
I just want to understand how the actor works in akka. So let's move on.
Try to understand the Design and Implementation
Import the package:
import akka.actor._
import akka.routing.RoundRobinRouter
import akka.util.Duration
import akka.util.duration._
Design
Master ----> Worker(N)
<----
Master ----> Listener
Messages:
Calculate - sent to the Master actor to start the calculation
Work - send from the Master actor to the Worker actors
Result - send from the Worker actors to the Master actor
PiApproximation - send from the Master actor to the Listener actor
Messages sent to actors should always be immutable to avoid sharing mutable state. 'case class' make excellent messages.
sealed trait PiMessage
case object Calculate extends PiMessage
case class Work(start: Int, nrOfElements: Int) extends PiMessage
case class Result(value: Double) extends PiMessage
case class PiApproximation(pi: Double, duration: Duration)
A sealed trait can be extended only in the same file than its declaration.
Creating the worker
class Worker extends Actor {
//get the start and number
def calculatePiFor(start: Int, nrOfElements: Int): Double = {
var acc = 0.0
for (i <- start until (start + nrOfElements))
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1)
acc //return the latest line
}
//receive the task data from message, sender back once we are done
def receive = {
case Work(start, nrOfElements) =>
sender ! Result(calculatePiFor(start, nrOfElements)) // perform the work
}
}
We send the message back to the sender using the sender reference.
In Akka the sender reference is implicitly passed along with the message so that the receiver can always reply or store away the sender reference for future use.
Creating the master
The master actor is a little bit more involved.
class Master(nrOfWorkers: Int,
nrOfMessages: Int,
nrOfElements: Int,
listener: ActorRef) extends Actor { // listener is a ActorRef we can send message
var pi: Double = _
var nrOfResults: Int = _
val start: Long = System.currentTimeMillis //get the start time
//define the number of workers and using round-robin router
val workerRouter = context.actorOf(
Props[Worker].withRouter(RoundRobinRouter(nrOfWorkers)), name = "workerRouter")
def receive = {
case Calculate =>
for (i <- 0 until nrOfMessages)
workerRouter ! Work(i * nrOfElements, nrOfElements) // divide the tasks, send all tasks to workerRouter
case Result(value) => // case it is the sender message about result from the worker
pi += value
nrOfResults += 1
if (nrOfResults == nrOfMessages) { //get all the messages back
listener ! PiApproximation(pi, duration = (System.currentTimeMillis - start).millis)
context.stop(self) // stop the master actor
}
}
}
Our master have 3 parameters.
nrOfWorkers how many workers we should start up
nrOfMessages how many chunks to send out to the workers
nrOfElements how big the number chunks send to each worker
The ActorRef is used to report the final result to the outside world.
References:
http://doc.akka.io/docs/akka/2.1.0/
http://doc.akka.io/docs/akka/2.1.0/general/index.html
https://github.com/typesafehub
http://typesafe.com/resources/tutorials/getting-started-with-akka-scala.html#getting-started-with-akka-scala
http://alvinalexander.com/scala/java.lang.nosuchmethoderror-scala.predef.augmentstring-error
http://stackoverflow.com/questions/11203268/what-is-a-sealed-trait