Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Akka TypedActor vs. writing my own static interface to an Actor class

I've been using Akka and Scala for about a month and I'm somewhat bothered by replacing explicit interfaces with messages. Consider the following simple Akka Actor:

case class DoMyHomework() class Parent extends Actor {   def receive = {     case d: DoMyHomework => // do nothing   } } 

Actor or non-actor code that sends this actor a DoMyHomework message like this:

ActorRef parent = ... parent.ask(DoMyHomework) 

Has no idea what the outcome will be. What's the type of the answer? Will I ever get an answer? Can I get an exception? And so on.

The fix seems to be to document the case class... but what if some other actor also receives that same case class. Then the documentation should for receiving that message should be in the actor itself.

In an effort to clean this up a little I thought of doing the following:

trait SomeoneSmarter {   def wouldYouDoMyHomework: Future[Boolean]  } class Parent extends Actor with SomeoneSmarter {   case class DoMyHomework()   def wouldYouDoMyHomework = {     (self ? DoMyHomework()).mapTo(Boolean)   }   def receive = {     case d: DoMyHomework =>       // TODO: If I'm busy schedule a false "No way" reply for a few seconds from now.       // Just to keep their hopes up for a while. Otherwise, say sure right away.   } } 

So, I chatted with colleagues about this and one of the reactions was "you're not being true to the actor model."

First, I would really appreciate some guidance from folks that have been using Actors for a longer time. Do all the messages become unwieldy? Do you end up hiding message-passing behind interfaces?

The actors I'm proposing still have the option of sending messages among themselves, subscribing to event streams, all the stuff you expect from Akka. And the interface gives you a time-tested way of knowing what you're talking to. And it helps when coding in IDEs, and so on. And why should the user of an actor need to know it's an actor (unless it's also an actor and is very tightly coupled with it)?

The other reaction I got was "it looks like you want a TypedActor". But after reading about TypedActor I'm not convinced. Certainly TypedActor saves me the trouble of creating those internal messages. But, at least from the code sample at http://doc.akka.io/docs/akka/snapshot/scala/typed-actors.html I get the impression that the TypedActor is meant only to work as a proxy around a block of code you want to encapsulate, or make thread-safe, or simply not call directly from your current thread. And what you code is just the implementation and interface. You don't mess with the actor itself (the proxy) - e.g. if you want your implementation to do periodic work or subscribe to an event stream, or do anything else unrelated to the interface.

I've also read http://letitcrash.com/post/19074284309/when-to-use-typedactors and didn't find that example more illuminating. I'm probably just not grokking TypedActor (not that I claim to have really understood Actors yet).

thanks in advance for help.

Pino

like image 969
Pino Avatar asked Sep 20 '12 16:09

Pino


People also ask

How can I send a message to an actor in Akka?

1) Akka Actor tell() Method It works on "fire-forget" approach. You can also use ! (bang) exclamation mark to send message. This is the preferred way of sending messages.

Are Akka actors thread-safe?

When an actor receives a message, it enqueues that message in it's mailbox. This process is thread safe and akka manages it behind the scenes.

Is Akka widely used?

Akka framework is widely used to build applications because the applications built by Akka implement concurrency and multi-threading. Applications that are deployed in the real world should contain features like concurrency or multi-threading because they will serve multiple clients at a time.

Should I use Akka?

Any system with the need for high-throughput and low latency is a good candidate for using Akka. Actors let you manage service failures (Supervisors), load management (back-off strategies, timeouts and processing-isolation), as well as both horizontal and vertical scalability (add more cores and/or add more machines).


2 Answers

Actor Encapsulation

Let me first respond to one point which I think is very important. You say:

And why should the user of an actor need to know it's an actor (unless it's also an actor and is very tightly coupled with it)?

Actors are a radically different programming paradigm from conventional OO, the main difference is that everything is asynchronous and hence there never are real “return values”. This means that it is usually a bad idea to hide the fact that it is an actor, for exceptions refer to my TypedActors blog post. The best thing about actors is that they are fully encapsulated—in Akka behind ActorRef—as opposed to the weak encapsulation of OO languages. To get the most out of it, expose ActorRefs wherever possible, which gives client code the opportunity to use them in the most appropriate way (which may be using tell or ask depending on the context).

Structuring your Messages

When writing an actor, you should put everything about this actor in one place, including the interface contract description. It could look a bit like this:

object Parent {   /**    * Send this message to make your parent do your homework … yeah, right ;-)    */   case object DoHomework }  /**  * This actor will do your homework if asked to.  *   * ==Actor Contract==  *   * ===Inbound Messages===  *  - '''DoHomework''' will ask to do the homework  *   * ===Outbound Messages===  *  - '''HomeworkResult''' is sent as reply to the '''DoHomework''' request  *   * ===Failure Modes===  *  - '''BusinessTripException''' if the parent was not home  *  - '''GrumpyException''' if the parent thinks you should do your own homework  */ class Parent extends Actor {   … } 

Differences to TypedActor

Using normal untyped actors lets you make use of the full power of the actor model, including dynamically changing the behavior, and not being tempted to box yourself into the timeout-guarded cage of “synchronous” calls (in short, TypedActors are mostly useful when implementing a traditional synchronous interface using actors behind the scenes). I agree that IDE support for message types would be nice, but that is a tool problem (I have been talking with the ScalaIDE team about adding some magic, but that will have to wait until it can receive priority). Having all properties about the actor defined in one place is the important part.

like image 143
Roland Kuhn Avatar answered Oct 04 '22 11:10

Roland Kuhn


Disclaimer: I'm not an Akka/Actors expert. I've been working with Actors and Akka about 18 months and I'm still trying to wrap my head around certain concepts especially when not to use Akka.

For the particular and narrow case that you want to know the return type of an Akka future, yes, you should use a TypedActor. The few times I've used TypedActors they were used to provide an API to a module that fell outside the Actor system. That is, I've built a system on top of Akka that did most of its work inside an Akka network but had one or two modules outside of the Akka network that required access to the features provided by the Akka network. The most notable was a Scalatra frontend that called into the Akka network and did some work on the values returned by the Akka network before responding to its client. The TypedActor, however, was really just a frontend to the Akka network. I look at using a TypedActor as an API frontend to external (external to the Akka network) modules as another separation of concerns.

In general, I agree with those that are telling you that "you're not being true to the actor model" in trying to coerce a view of its return types. In its purest form, and the way I've had the most success, the Actor model is implemented using fire and forget semantics. The messages do not get unwieldy and in many cases they've helped organize my code and define work boundaries. It does help to put them in their own package.

Were I to implement the feature you've described it would look like the following:

trait SomeoneSmarter {    def wouldYouDoMyHomework : Boolean   }  class Response() case class NoWay() extends Response case class Sure() extends Response  class ActorNetworkFrontEnd extends Actor {    def receive = {     case d: DoMyHomework =>       busy match {         case true => sender ! NoWay()         case false => sender ! Sure()       }   } }  case class SomeoneSmarter(actorNetworkFrontEnd:ActorRef) extends SomeoneSmarter {    def wouldYouDoMyHomework : Boolean = {     val future = actorNetworkFrontEnd ? DoMyHomework()     val response = Await.result(future, timeout.duration).asInstanceOf[Response]     response match {       case NoWay() => false       case Sure() => true     }   }  } 

Keep in mind the way I've written wouldYouDoMyHomework, it will block while waiting for an answer. There are clever ways to do this asynchronously however. See http://doc.akka.io/docs/akka/2.0.3/scala/futures.html for more information.

Also, keep in mind that once your message is inside the Akka network you can do all of the cool scaling and remoting stuff and the user of your TypedActor API never has to know.

Doing this does add some complexity in large projects but if you consider it as seperating the responsibility of providing an API to external modules and maybe even move that responsibility to another package it's very easy to manage.

Good question. I can't wait to hear answers from the more experienced Akka developers.

like image 44
Eric Reichert Avatar answered Oct 04 '22 11:10

Eric Reichert