1. 介绍

本文主要参考文章:Quasar and Akka – a Comparison

本文主要就是总结翻译一下以上文章的观点,有些地方会精简。想查看详细的,建议可以看英文原文。

本文中的协程和纤程是一个意思哦。

2. actor并发模型

其实我之前在《七周七并发模型》的读书笔记中也总结过actor模型,可以看——第五章:Actor

基于actor并发模型主要有以下好处:

2.1 Adaptive behaviour(不知道怎样翻译准确)

actor之间只通消息队列来通信,这样可以让各个actor松耦合。基于这样的特点,有以下优点(都可以归纳为adaptive behaviour):

  1. 隔离故障(Isolate faults):通过引入信箱(管理消息队列)的概念,actor发布消息给信箱,解耦了多个消息队列,使得actor可以在不影响服务的情况下重启
  2. 管理进化(Manage evolution):可以在不重启服务情况下替换actor
  3. 调节并行(Regulate concurrency):可以控制信箱的大小来调节并行度
  4. 调节负载(Regulate load):调节接受调用的频次、信箱大小等手段调整系统负载

2.2 并行度大

  1. 内存和管理开销很少,一个信箱管理100万个actor不成问题
  2. actor完全独立不共享状态,可以安全的并行(这个用到了JAVA并发编程实战的知识啦)

2.3 低复杂度

  1. 每个actor因为是独立的,可以自由修改状态,不用担心并发修改的问题
  2. 通过有选择的从信箱接受消息来简化actor状态改变的逻辑,而不是使用消息抵达顺序

接下来我们就要来讨论基于JVM的实现actor模型的两个库akka和Quasar.

3. Quasar

Quasar是个基于JVM的简单轻量的开源协程库(只支持JAVA)。求主要特点如下:

  1. 使用简单:quasar使用起来和普通的线程没有区别
  2. 开销小:其内存和上下文切换的开销十分小。所以可以在一个JVM上很轻松的产生成千上万的协程。
  3. 支持内部通信:quasar协程内部通信仿照了GO语言设计了channel。
  4. 对actor实现细节透明:quasar协程本身实现参照Erlang完整实现了actor模型,但是使用起来很方面,不用关系底层actor模型实现细节。
  5. 迁移成本低:现有库集成quasar改动小

顺便推荐下comsat,它把quasar集成到了一些常见的框架、和其他项目中,例如kafka、spring boot等等。

3.1 quasar如何实现协程

通过创建和调度continuation任务来实现quasar协程。例如generators这种就是利用continuation任务来实现的。这种continuation任务是一种能以较小代价来暂停和恢复的任务(区别挂起线程)。在quasar中,方法要支持协程暂停任务,需要声明相关的注解,这样quasar就可以识别注解,然后通过修改字节码来暂停和恢复协程。这个和GO语言的协程实现思路接近。

参考资料:
1.次时代Java编程(一):Java里的协程

4. Akka

用scala写的一个actor模型实现库。前面的quasar是基于Erlang的思想实现的actor模型实现库。erlang风格的actor模型实现,主要是基于协程。而akka则是自己实现了一个异步、基于回调的actor DSL。akka中并没有轻量级的协程这种概念。其实一个完整的服务框架,从配置、部署到测试。

5. 阻塞与非阻塞的比较

Akka: 使用非阻塞API,一个actor在收到信息后是回调触发receive方法的。
Quasar(Erlang,Go,Clojure):使用阻塞API,一个actor调用receive方法,阻塞直到收到信息

一般来说,阻塞都会带来一些系统线程开销,但是由于协程十分轻量,所以采用阻塞API也没事。阻塞API的好处是代码写起来更加简单,也更加好维护。

6. API比较

7. Actor定义(只演示JAVA语法)

7.1 Quasar

Quasar actors – like Erlang processes – use blocking receive, and are allowed to perform blocking IO (although blocking IO operations don’t end up blocking OS threads, but rather fibers, which makes them very scalable).

class MyActor extends BasicActor<String, MyActorResult> {
    private final Logger log = LoggerFactory.getLogger(MyActor.class);

    @Suspendable
    @Override
    protected MyActorResult doRun() throws InterruptedException, SuspendExecution {
        // ...Arbitrary code here...
        final String msg = receive(m -> {
            if ("test".equals(m)) return "testMsg";
            else return null; // Defer
        });
        // ...Arbitrary code here...
        return new MyActorResult();
    }
}

7.2 akka

Akka actors are implemented as a callback to a receive event, and are not allowed to block:

public class MyUntypedActor extends UntypedActor {
  LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  // "receive" must be toplevel
  public void onReceive(Object message) throws Exception {
    if ("test".equals(message))
      log.info("received test");
    else
      log.info("received unknown message")
  }
}

8. Actor声明周期 和管理分层

actor管理和监控在Quasar和Akka中都是支持的。详情可以看原文。

9. 其他对比以及我的总结

文章还从性能对比、系统监控管理、代码测试、热升级、网络、可靠性和集群等方面对两者进行对比。对比结果可以用以下几句话概括:

  1. 性能上akka略微好点,但是消耗资源稍微多点。
  2. akka属于自己搞一套,quasar和原来的JAVA生态融合度更好,actor实现上也更加传统符合驻留(模仿go、erlang的设计)

我的选择——quasar。原因:

  1. JAVA写的,我对JAVA比较熟
  2. 和JAVA生态融合好
  3. 轻量、简单但是又五脏俱全
  4. 学习成本低,主流实现思想在go、clojure和erlang中都适用

此外并发之痛 Thread,Goroutine,Actor这篇文章也写的不错,推荐看看。