Reactive Programming with Scala+Akka+Play
— Scala — 4 min read
Since I wanted to learn Scala and it’s actor based concurrency model I wrote a browser based game called WIKIME. You can play it at www.wikime.in and check out the source code at github. In this article I want to wrap up how I implemented all possible long running task asynchronously and in a non-blocking way.
I do not want to talk about the overall architecture but in case you want to clone it an run it locally ... it is very
easily done with Play. When you have Play installed locally and checked out the source of
WIKIME you can just start it by executing play run
in the source’s root directory.
So what’s the game about?
Put simply, the goal is to get a chosen Wikipedia article show up in your browser. Time matters and also the amount of hyperlinks you need to get there. Of course there are some conditions to be complied with which condense to:
- first: You start out from a random Wikipedia article.
- second: You must only use links to Wikipedia articles.
- third: You must not use Wikipedia’s search box.
- fourth: The chosen article must be opened through the WIKIME Wikipedia proxy.
In order to take away most of the obvious temptations such as copy and paste the article’s URL into your browser or using Wikipedia’s search box I wrote a proxy to Wikipedia. When opening a Wikipedia article through the WIKIME proxy at least the two mentioned ways of cheating are impossible.
How does it work?
Figure 1 – Wikime in full action.
After the login you can challenge any idle player to “race” to a Wikipedia you chose. When your opponent accepts the challenge both of you get a random page from Wikipedia loaded into an iframe. By then you should start to follow the page’s links to the page you agreed to run to. At any time you can see on which article your opponent is in the right smaller iframe. Your opponents iframe you can scroll up and down but cannot click since all links are disabled for you. The one who is first at the finish page wins. The amount of links and the seconds you needed to get there adds to up your highscore. Wikime
Technical details
All browser-server communication other than page loads is handled through a Websocket connection. On every page load a Websocket connection is created on the browser side and a site specific message in json format is s ent to the server. The Websocket connection is cached on the server side and only destroyed when the connection is closed for more than ten seconds.
All actions on the server that are triggered by a websocket message or by opening a game page run asynchronously and non-blocking. I want to explain in more depth how I connect Play’s Websocket implementation and it’s non-blocking routing controller methods to Wikime’s Actor hierarchy (Figure 1) that functions as my game engine. actor hierarchy
Figure 2 – Wikime’s Actor hierarchy
Let’s have a look at the Websocket connection first (Listing 1). Setting up the server side of the Websocket is very easy thanks to Play. Basically you only have to create a tuple of an Enumerator and an Iteratee, which is not much more than a producer/consumer pair. Check out Pascal Voitot’s detailed explanation of how they work.
Since the Websocket connection would be closed on every page reload I have to cache it on the server side. I decided to use
the PlayerActor
(alice, bob, karl-heinz, annette18) as cache for the connection. In order to retrieve a specific player’s
Enumerator/Iteratee
-pair I use the ask pattern
and forward the Connect(uuid, username(request))
-message from WikimeActor
over PlayersActor
who creates or looks up the corresponding PlayerActor
to finally the PlayerActor
who sends back his
two members that make up the Websockets Enumerator/Iteratee-pair.
Since the ask
-method on a ActorRef
returns a Future[Any]
I only need to map it to
(Iteratee[JsValue, _], Enumerator[JsValue])]
and can use this value directly as a the block expression’s return value.
1def socket() = WebSocket.async[JsValue] {2 implicit request =>3 uuid(request) match {4 case Some(uuid) =>5 implicit val timeout = Timeout(Duration(3, TimeUnit.SECONDS))6 (wikimeActor ? Connect(uuid, username(request)))7 .mapTo[(Iteratee[JsValue, _], Enumerator[JsValue])]8 case None => Future(9 (Done[JsValue, Unit]((), Input.EOF),10 Enumerator[JsValue](11 JsObject(Seq("error" -> JsString("could not connect"))))12 .andThen(Enumerator.enumInput(Input.EOF))))13 }14}
Listing 1 – Websocket’s controller method.
The PlayerActor
is not only a cache for the Websocket connection but also for the player’s states in general.
While playing the player’s state is defined by a single page on Wikipedia and the pages he has clicked through before.
The game
method is the entry point to update a player’s state. Since I do not want to block one thread per player-update call
I implemented the game
-method in a non-blocking way.
I implemented the call chain in the same manner as explained in Websocket connection using the ask
-pattern with one
little difference.
A requirement was to route differently dependend on the returned player’s state.
For example if a player manually enters a non valid URL, this should be punished as a tilt (funny).
Further I did not want to bother the PlayerActor
with knowning about any Play mvc routings.
So instead of returning the Future
the ask
-method provides I bind a callback to that future with onComplete
(line 8).
The callback closes over the previously constructed Promise
(line 7) and sets the corresponding future to the right
play.api.mvc.SimpleResult
(line 11,13 and 17). The promise’s future is then used as a return value of the game
-method.
1def game(playerOne: String, playerTwo: String, page: String, next: String) = Action.async {2 implicit request =>3 implicit val timeout = Timeout(Duration(10, TimeUnit.SECONDS))4 username(request) match {5 case Some(username) =>6 val r = (wikimeActor ? Open(GameKey(playerOne, playerTwo, page), username, next))7 val p: Promise[play.api.mvc.SimpleResult] = promise[SimpleResult]8 r.mapTo[GameState] onComplete {9 case Success(state) =>10 if (state.tilted) {11 p success Ok(views.html.game(username, playerOne, playerTwo, page, state.back.getOrElse(page), state))12 } else {13 p success Ok(views.html.game(username, playerOne, playerTwo, page, next, state))14 }15 case Failure(ex) =>16 Logger.error("cannot open game page")17 p success Redirect(routes.Application.index)18 }19 p.future20 case _ => future { Unauthorized }21 }22 }
Listing 2 – Async controller method.
The most time consuming operation during updating a player’s state is downloading the page on Wikipedia where the player
wants to go next. In order to keep the PlayerActor responsive during the update I had to implement the call to my
Wikipedia client also in a non-blocking way.
The solution I found is described by Jamie Allen as the Extra Pattern (Effective Akka).
The Extra Pattern uses an anonymous actor to capture context that is needed at a later time.
In line 13 of Listing 3 the val originalSender
is assigned with sender
.
The originalSender
val is used inside of the anonymous Actor that fetches Wikipedia content and returns to the
val sender
at the time the anonymous actor was created.
1case open @ OpenWithGameState(gameState, next) =>2 if (!state.isInstanceOf[Playing]) {3 Logger.error("cannot only react on Open while playing")4 sender ! akka.actor.Status.Failure(new IllegalStateException)5 }67 val playing = state.asInstanceOf[Playing]8 if (next == playing.at.name) {9 Logger.debug(s"already downloaded $next")10 sender ! gameState11 } else {12 var originalSender = sender13 context.actorOf(Props(new Actor() {14 def receive = {15 case OpenWithGameState(gameState, next) =>16 if (!isAllowed(next)) {17 gameState.increment("you", 10)18 val gameActor = context.actorSelection(s"/user/wikime/games/${playing.gameKey.actorPath}")19 gameActor ! Tilt(username.get)2021 originalSender ! GameState.tilt(gameState, playing.back)22 } else {23 Wikipedia.open(next) match {24 case Some(article) =>25 update(article)26 gameState.increment("you")27 Logger.debug(s"new gameState $gameState")28 originalSender ! gameState29 val playersActor = context.actorSelection("/user/wikime/players")30 playersActor ! PushMirror(playing.other, article, gameState.counts.getOrElse("you", 1))3132 val gameActor = context.actorSelection(s"/user/wikime/games/${playing.gameKey.actorPath}")33 gameActor ! UpdateTransitions(username.get, article.name)3435 context.stop(self)36 case None =>37 Logger.error(s"could not open $next")38 sender ! akka.actor.Status.Failure(new IllegalStateException)39 context.stop(self)40 }41 }42 }43 })) ! open44 }
Listing 3 – The Extra Pattern.
Finally we come to the Gretchen question: “Can I call Wikime reactive with clear consciounce according to the reactive manifesto?” By the reactive manifesto an application would be reactive when it respects the following properties (Listing 4).
- React to events: the event-driven nature enables the following qualities.
- React to load: focus on scalability rather than single-user performance.
- React to failure: build resilient systems with the ability to recover at all levels.
- React to users: combine the above traits for an interactive user experience.
Listing 4 – Properties of an reactive application.
"React to events", "React to load" and "React to users" are met, since I made sure no single user can block the server from making progress. Each step that could need more computation- or io-time is either implemented in a fire-and-forget way or uses futures and promises to complete. However, neither do I define possible system "failures" nor do react to them. So that makes Wikime 3/4 reactive. I say that is not to bad. And hopefully I will understand at the Reactive Programming course at coursera more about how to make Scala/Akka apps embrace failure in order to be fully reactive.