本文要点
Actor模型为编写并发和分布式的系统提供了高层次的抽象,为开发人员屏蔽了显式锁定和线程管理的工作;
Actor模型为反应式系统提供了核心功能,这些功能在反应式宣言中定义为响应性、弹性、扩展性以及消息驱动;
Akka是一个基于Actor的框架,借助Java 8的Lambda支持它非常易于实现;
通过使用Actor模型,开发人员在设计和实现系统时,能够更加关注于核心功能,忽略其他业务不相关的冗余内容;
基于Actor的系统非常适合快速演化的微服务架构。
尽管“反应式(reactive)”这个术语已经存在很长时间了,但是只有到最近它才被行业实际应用到系统设计之中,并得到了主流的采纳。在2014年Gartner就写到,过去非常流行的三层架构已经日薄西山。随着企业在推进现代化方面的努力,这一点已经越发明晰了,企业必须要重新思考他们十多年来构建应用的方式。
微服务席卷了软件行业,它所带来的冲击波正在从根本上动摇传统开发流程。我们看到软件设计范式发生了变化,项目管理的方法论也随之发生了演化。我们正在向新的应用设计和实现方式转变,它以前所未有的势头在IT系统中实现。即便微服务这个术语不是全新的概念,我们的行业也正在意识到它不仅仅是解耦RESTful端点和拆分单体应用,它真正的价值在于更好的资源利用效率以及面对不可预知工作负载时更强的扩展性。反应式宣言(Reactive Manifesto)的原则很快变成了微服务架构的圣经,因为它们本质上就是分布式的反应式应用。
如今应用中的Actor模型
为了保持用户的兴趣,应用必须要保持很高的响应性,同时,为了满足受众不断变化的需求和预期,应用必须要快速演化。用于构建应用的技术在不断地快速演进;科学在不断发展,持续涌现的新需求不能依赖于昨天的工具和方法论。Actor模型正在不断发展起来,它是一种构建应用的高效工具,能够充分发挥多核、内存以及集群环境所带来的强大处理能力。
Actor提供了一种简单却强大的模型,通过该模型设计和实现的应用可以分布式的,并且能够跨系统中所有的资源共享工作任务,这些资源可以从线程和核心级别一直到服务器集群和数据中心级别。它提供了一个高效的框架来构建应用,所构建出的应用具有较高的并发性,并且能够提升资源的利用率。另外很重要的一点,Actor模型还提供了定义良好的方式来优雅地处理错误和故障,确保应用的可靠性级别,它能够隔离问题,防止级联故障和长时间的宕机。
在过去,构建高并发的系统通常涉及到大量的装配和非常技术化的编程,它们都是非常难以掌握的。这些技术方面的挑战会抢占我们对系统核心业务功能的注意力,因为很大一部分的工作都集中在业务细节上,这需要花费很多的时间和精力用于搭建处理管道和功能装配。如果我们使用Actor来构建系统的话,就能在一个较高的抽象层级完成这些任务,因为处理管道和功能装配已经内置在了Actor模型之中。这不仅能够将我们从繁琐的传统系统实现的细节中解放出来,还能让我们更加关注于系统的核心功能和创新。
使用Java 8和Akka实现Actor模型
Akka是一个在JVM上构建高并发、分布式、有弹性的消息驱动应用的工具集。Akka “actor”只是Akka工具集中一部分,它能够让我们在编写并发代码时,不用去思考低层级的线程和锁。Akka中其他的工具还包括Akka Streams和Akka http。尽管Akka是使用Scala编写的,但是它也有Java API,如下的样例运行在2.4.9版本以上(目前Akka的最新版本为2.5.7,但核心API与本文基本相同——译者注)。
在Akka中,Actor是基本的工作单元。Actor是状态和行为的一个容器,它可以创建和监管子Actor。Actor之间通过异步的消息实现相互的通信。这个模型保护了Actor的内部状态,使其能够实现线程安全,该模型还实现了事件驱动的行为,从而不会阻塞其他的Actor。作为开始,我们所需要知道的只是akka的Maven依赖。
com.typesafe.akka
akka-actor_2.11
2.4.9
改变Actor的状态
就像通过移动设备收发短信一样,我们需要使用消息来调用Actor。与短信类似,Actor之间的消息也必须是不可变的。在使用Actor的时候,最重要的就是定义它所能接受的消息。(这种消息通常被称为协议,因为它定义了Actor之间的交互点。)Actor接收消息,然后以各种方式对其作出反应,它们可以发送其他的消息、修改自己的状态或行为、创建其他的Actor。
Actor的初始行为是通过实现receive()方法来定义的,在实现这个方法时,可以在默认的constructor. receive()中借助ReceiveBuilder匹配传入的消息并执行相关的行为。每条信息的行为通过一个Lambda表达式来定义。在下面的样例中,ReceiveBuilder使用了对接口方法“onMessage”的引用。onMessage方法增加了一个计数器(内部状态)并通过AbstractLoggingActor.log方法记录了一条info级别的日志信息。
static class Counter extends AbstractLoggingActor {
static class Message { }
private int counter = 0;
{
receive(ReceiveBuilder
.match(Message.class, this::onMessage)
.build()
);
}
private void onMessage(Message message) {
counter++;
log().info("Increased counter " + counter);
}
}
Actor就绪之后,还需要启动它。这需要通过ActorSystem实现,它控制着Actor的生命周期。但是,我们首先需要提供一些关于如何启动这个Actor所需的额外信息。akka