喜欢和它的错误内核

Akka and its Error Kernel

我正在阅读 Akka (Java lib) 文档,需要澄清一些他们自己宣称的 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.

那么 Akka/Java 中的代码示例是什么样的?如果 Actor 不适合放置 必须 阻塞的代码,那么 所做的 满足 "some special-cased thread"?

Do not pass mutable objects between actors. In order to ensure that, prefer immutable messages.

我熟悉如何制作不可变的 classes(没有 public 设置器,没有 public 字段,使 class final, ETC。)。但是 Akka 有自己的 "immutable class" 定义吗?如果有,它是什么?

Top-level actors are the innermost part of your Error Kernel...

我什至不知道这是什么意思!我理解他们所说的 "top-level" 演员(在 actor/manager/supervisor 等级中最高)的意思,但是什么是 "Error Kernel",它与演员有什么关系?

我只能回答第一个问题(以后请在 post 中只填一个问题)。 例如,考虑一个固有阻塞的数据库连接。为了允许参与者连接到数据库,程序员应该创建一个带有数据库请求队列的专用线程(或线程池)。请求包含数据库语句和对接收结果的参与者的引用。专用线程循环读取请求,访问数据库,将结果发送给引用的actor等。请求队列阻塞——当没有请求时,连接线程阻塞在queue.take()操作中。

因此,对数据库的访问分为两个参与者 - 一个向队列发出请求,另一个处理结果。

更新:Java 代码草图(我不擅长 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);
    }
}

这是您第二个问题的答案。来自 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.

所以你说的这句话意味着这些演员是 "last line of defence" 来自你的监督等级制度的错误,所以他们应该是强壮有力的人(突击队)而不是一些软弱的工人。你拥有的突击队员越少——管理他们就越容易,避免高层出现混乱。准确地说,突击队的数量应该接近您拥有的业务协议的数量(转向超级英雄 - 假设一个用于 IronMan,一个用于 Hulk 等)

这篇文档也很好地解释了如何管理阻塞操作。

说起来

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 肯定不会,因为 Akka 只保证顺序性,但您的消息可以在任何线程上处理(它只是从池中获取一个空闲线程),即使对于单个 actor 也是如此。不建议在那里进行阻塞操作(至少在与正常线程池相同的线程池中),因为它们可能会导致性能问题甚至死锁。例如,参见 Spray 的解释(它基于 Akka):

你可能会认为 akka 需要只与异步交互 API。您可以考虑 Future 将同步转换为异步 - 只需将数据库的响应作为消息发送给参与者。 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
     }
 }

同一文档中描述了其他方法 (Akka Doc)