Dieser Vortrag stellt mit Play2, Scala und Akka einen Technologiestack vor, der es ermöglicht relativ einfach eine skalierbare Webarchitektur aufzubauen, die mit dem Web und nicht gegen das Web arbeitet. Insbesondere der Einsatz von Akka als flexible Messagingplattform bietet dabei einige Vorteile gegenüber konventionellen Thread basierten Lösungen in Bezug auf die horizontale Skalierung der Applikation.
9. Fallbeispiel
!
•
3 Rollen: Client, Master, Server!
•
interne Kommunikation über Akteure!
•
externe Kommunikation über Akteure und Rest/Webservices
10. Was ist ein Akteur?
Akteure kapseln Zustand, Verhalten, besitzen eine Mailbox
und bilden
Hierarchien.!
!
Akteure kommunizieren über Nachrichten.!
!
11. Codebeispiel
class ImageActor(id: String, directoryActorRef: ActorRef) extends Actor {!
!
implicit val ec = context.dispatcher!
!
val ticker = context.system.scheduler.scheduleOnce(3.minutes, self, ImageActor.TimeOutImageEvaluation)!
!
def receive = {!
case ImageActor.Evaluation(_, tags) => !
ticker.cancel!
log.info(s"received tags $tags for actor $self")!
sender ! DirectoryActor.EvaluationAccepted!
Nachrichten empfangen
!
case ImageActor.TimeOutImageEvaluation =>!
log.info("Image Expired, received time out")!
log.info("sending parent a ExpiredImageEvaluation message")!
directoryActorRef ! CommonMsg.ExpiredImageEvaluation(id)!
}!
}
Nachrichten senden
18. Codebeispiel
class MasterActor(serverNames: List[String])
extends Actor with ActorLogging with Configured {
Bekommt der MasterActor eine Anfrage nach einer ImageId, !
sendet er die Anfrage an den RoundRobinRouter weiter.
!
…
val roundRobinRouter =
context.actorOf(Props.empty.withRouter(
RoundRobinRouter(routees = serverRoutees)),
"RoundRobinRouter")
Konfiguration
!
def receive = LoggingReceive {
case RequestImageId =>
roundRobinRouter forward RequestImageId
case … => …
}
!
}
Verwenden
20. Codebeispiel
class MasterActor(serverNames: List[String])
extends Actor with ActorLogging with Configured {
!
…
!
val broadCastRouter =
context.actorOf(Props.empty.withRouter(
BroadcastRouter(routees = serverRoutees)),
"BroadCastRouter")
!
def receive = LoggingReceive {
…
!
case Ping =>
broadCastRouter forward Ping
}
!
}
Der MasterActor fragt die einzelnen Server periodisch ab!
und leitet Ping Anfragen an alle Server weiter.
Konfiguration
Verwenden
22. Codebeispiel
Erhält der MasterActor eine Evaluation (d.h. eine Liste vonTags für ein Bild),!
leitet er die Anfrage weiter !
class MasterActor(serverNames: List[String])
und bekommt eine Antwort vom ersten Server, !
extends Actor with ActorLogging with Configured {
!
der sich dafür zuständig erklärt.
…
val scatterGatherRouter =
context.actorOf(Props.empty.withRouter(
ScatterGatherFirstCompletedRouter(routees = serverRoutees,
within = appConfig.defaultTimeout)),
"ScatterGatherRouter")
Konfiguration
!
case e: Evaluation =>
log.info("""
forwarding evaluation to scatterGatherRouter
""")
scatterGatherRouter forward e
!
}
!
}
Verwenden
23. Circuit Breaker => Failure
Open
Closed
Halfopen
•
Circuit Breaker haben einen definierten Timeout und einen Retry Counter!
•
Alles gut? => Closed state!
•
Timeout und Counter erreicht => Open State!
•
Open State und Request => probiere einen Request alle andere werden abgelehnt!
•
Half Open und Request => Alles Gut? => Closed sonst Open
24. Codebeispiel
Der Master verlangt vom ImageServer eine neue ImageId.!
!
!
/**
Schlägt der Aufruf 3x fehl (maxFailures),
* Represents a server serving images.
werden weitere Aufrufe sofort mit Fehler zurückgemeldet,
*
*/
bis sich der CirucitBreaker wieder schliesst.
class ServerActor(url: String) extends Actor with ActorLogging {
…
val breaker =
new CircuitBreaker(context.system.scheduler,
Konfigurieren
maxFailures = 3,
callTimeout = 1 seconds,
resetTimeout = 30 seconds)
def receive = LoggingReceive {
case RequestImageId =>
breaker.withCircuitBreaker(WS.url(imageUrl).get) pipeTo sender
!
Verwenden
case …
}
}
25. Akteur Supervisor => Failure
!
Controller
reply
REST
Client
Master
Jeder Akteur hat einen zugewiesenen
Supervisor.!
RequestImage
•
OneForOneStrategy: Supervisor
behandelt den Fehler für den
fehlgeschlagenen Akteur!
•
AllForOneStrategy: Supervisor
behandelt den Fehler für alle
„Siblings“
forward RequestImage
Exception
via Router
Server
Server
Server
26. Codebeispiel
// DirectoryActor receives Notification if it „died“ and the Supervisor handles the exception according
// to the plan
…
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: UnsupportedOperationException
Resume
case _: FileNotFoundException
case _: ActorKilledException
case _: Exception
Escalate
}
…
Exceptions
Restart
Stop
Konfiguration
27. Apache Camel
Framework zur Umsetzung von Enterprise Integration Patterns!
/**
* Camel Producer endpoint which is configured via [[util.AppConfig.camelEndpoint]].
*/
class CamelActor extends Actor with Producer with Oneway with Configured with ActorLogging{
!
lazy val appConfig = configured[AppConfig]
def endpointUri = appConfig.camelEndpoint // e.g. "activemq:evaluations"
!
}
…
// send Json as Inputstream to CamelActor
val is = IOUtils.toInputStream(Json.prettyPrint(Json.toJson(eval)))
val headerMap = Map(Exchange.FILE_NAME -> extractFileName(id))
camelActor ! CamelMessage(body = is, headers = headerMap)
or „file://tmp/camel/…“