I am reading the Akka (Java lib) docs and need clarification on some of their own proclaimed Akka/Actor Best Practices.
Actors should not block (i.e. passively wait while occupying a Thread) on some external entity...The blocking operations should be done in some special-cased thread which sends messages to the actors which shall act on them.
So what does a code example of this look like in Akka/Java? If an Actor
isn't an appriote place to put code that has to block, then what does satisfy the definition of "some special-cased thread"?
Do not pass mutable objects between actors. In order to ensure that, prefer immutable messages.
I'm familiar with how to make immutable classes (no public setters, no public fields, make the class final
, etc.). But does Akka have its own definition of an "immutable class", and if so, what is it?
Top-level actors are the innermost part of your Error Kernel...
I don't even know what this means! I understand what they mean by "top-level" actors (highest in the actor/manager/supervisor hierarchy), but what's an "Error Kernel", and how does it relate to actors?
I am able to answer only the first question (and in future, please place only one question in a post).
Consider, for example, a database connection, which is inherently blocking. In order to allow actors to connect to a database, programmer should create a dedicated thread (or a thread pool) with a queue of database requests. A request contains a database statement and a reference to the actor which is to receive the result. The dedicated thread reads requests in a loop, accesses the database, sends the result to the referenced actor etc. The request queue is blocking - when there are no requests, the connection thread is blocked in the queue.take()
operation.
So the access to a database is split in two actors - one places a request to the queue, and the other handles the result.
UPDATE: Java code sketch (I am not strong in Scala).
class Request {
String query;
ActorRef handler;
}
class DatabaseConnector implements Runnable {
LinkedBlockingQueue<Request> queue=new LinkedBlockingQueue<Request>();
Thread t = new Thread(this);
{t.start();}
public void sendRequest(Request r) {
queue.put(r);
}
public void run() {
for (;;) {
Request r=queue.take();
ResultSet res=doBlockingCallToJdbc(r.query);
r.handler.sendOneWay(res);
}
}
Here is the answer for your second question. Right from the Akka Doc:
If one actor carries very important data (i.e. its state shall not be lost if avoidable), this actor should source out any possibly dangerous sub-tasks to children it supervises and handle failures of these children as appropriate. Depending on the nature of the requests, it may be best to create a new child for each request, which simplifies state management for collecting the replies. This is known as the “Error Kernel Pattern” from Erlang.
So the phrase you talking about means that these actors are the "last line of defence" from errors in your supervision hierarchy, so they should be strong and powerful guys (commandos) instead of some weak workers. And the less commandos you have - the easier it would be managing them and avoid mess at the top-level. Precisely saying, the count of commando's should be near to the count of business protocols you have (moving to the superheroes - let's say one for IronMan, one for Hulk etc.)
This document also has a good explanation about how to manage blocking operations.
Speaking of which
If an Actor isn't an appriote place to put code that has to block then what does satisfy the definition of "some special-cased thread
Actor definetely doesn't, because Akka guarantees only sequentiality, but your message may be processed on any thread (it just picks-up a free thread from the pool), even for single actor. Blocking operations are not recommended there (at least in same thread-pool with normal) because they may lead to performance problems or even deadlocks. See explanation for Spray (it's based on Akka) for instance : Spray.io: When (not) to use non-blocking route handling?
You may think of it like akka requires to interact only with asynchronous API. You may consider Future for converting sync to async - just send response from your database as a message to the actor. Example for scala:
receive = { //this is receiving method onReceive
case query: Query => //query is message safely casted to Query
Future { //this construction marks a peace of code (handler) which will be passed to the future
//this code will be executed in separate thread:
doBlockingCallToJdbc(query)
} pipeTo sender //means do `sender ! futureResult` after future's completion
}
}
Other approaches are described in the same document (Akka Doc)
来源:https://stackoverflow.com/questions/29611444/akka-and-its-error-kernel