Akka(2)Start with first Project - First Part

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



你可能感兴趣的:(start with)