一、初识 Actor

本贴最后更新于 2064 天前,其中的信息可能已经渤澥桑田

一、初识 Actor

1.1 初识 Actor

Actor模型 是一种并发计算的理论模型,而 Akka 的核心其实是 Actor 模型的一种实现。Akka 借鉴了 Erlang 的 Actor 模型实现,同时又引入了许多新特性,能够构建处理如今大规模问题的应用程序。据说 Akka 一词来源于瑞典的一座冰山。

Actor模型 中,Actor 是一个并发原语。更简单的来说,可以把每个 Actor 都看做一个独立的人,每个人都各司其职。就像能够工作或是处理任务的进程或线程一样。这样就没有那么多需要共享的数据。就像你不希望你的银行卡密码被第二个人知道。

1.1.1 Actor 和消息传递

在面向对象编程中,对象的特点之一就是能够被调用,一个对象可以访问或修改另一个对象的属性,也可以直接调用另一个对象的方法。这种情况在单线程模式下运行是没有问题的;但是在多线程的模式下就要对共享的数据进行加锁。

Actor对象 的不同之处在于其不能直接被读取、修改或调用。反之,Actor 只能通过 消息传递 的方式与外界进行通信。

Actor 每次只能同步处理一个消息。邮箱 本质上是等待 Actor 处理的 工作队列。处理一个消息时,为了能够做出响应,Actor 可以修改内部状态,创建更多的 Actor 或是将消息发送给其他 Actor。

通常用 Actor系统 表示多个 Actor 的集合以及所有跟 Actor 相关的东西,包括地址、邮箱及配置。

  • Actor:一个表示工作节点的并发原语,同步处理接收到的消息。Actor 可以保存并修改内部状态。
  • 消息:用于跨进程(比如多个 Actor 之间)通信的数据。
  • 消息传递:一种软件开发范式,通过传递消息来触发各种行为,而不是直接触发行为。
  • 邮箱地址:消息传递的目标地址,当 Actor 空闲时会从该地址获取消息进行处理。
  • 邮箱:在 Actor 处理消息前,存储消息的地方。实质是一个消息队列。
  • Actor 系统:多个 Actor 的集合,以及这些 Actor 的地址、邮箱及配置。

Actor 模型的好处之一就是可以 消除共享状态。因为一个 Actor 一次只处理一条消息,所以可以在 Actor内部 安全的保存状态。


1.1.2 Actor 容错机制

20 世纪 80 年代,爱立信在 Erlang 语言中实现了 Actor 模型,用于嵌入式电信应用程序;并且在该实现中引用了通过监督机制(Supervision)提供的容错性概念,使得 AXD301 这个应用能够提供 99.9999999% 的可用性。

Actor 模型也通过 监督机制 来提供 容错性。监督机制基本上是指把处理 响应错误 的责任 交给 出错对象 以外 的实体。这意味着一个 Actor 可以负责监督他的子 Actor,它会监控子 Actor 的运行错误,并且根据子 Actor 生命周期中的运行表现执行相应的操作。当一个正在运行的 Actor 发生错误时,监督机制 提供 默认 的处理方式是 重新启动 发生错误的 Actor(实际上是重新创建)。

这种重新创建出错 Actor 的处理方式基于一种假设:意外发生的错误是由错误状态导致的,因此移除并重新创建应用程序中出错的部分可以将其修复,并回复正常工作。我们也可以编写自定义的错误处理方式作为监督策略,这样一来基本上就可以采取任何操作将应用程序回复至工作状态。
QQ 截图 20170926153331.png-22.6kB


1.2 创建一个 Akka 应用

创建一个 Actor ,该 Actor 接收一个消息,将消息的值存入 map 以修改 Actor 的内部状态。

1.2.1 构造消息结构

Actor 需要从其 邮箱 中获取消息,并 查看 消息中的操作提示。通过消息的 类/类型 来决定具体的操作。
  消息必须永远是不可变的,这样可以避免通过 多个执行上下文/线程 来做一些不安全的操作,从而避免一些奇怪而又出人意料的行为。同样要记住这些消息除了会发给本地的 Actor 之外,也可能会发给另一台机器上的 Actor。一般来说,应该始终在所有代码中优先使用不可变对象。

package com.shiw.ak;

/**
 * Created by Sweeney on 2017/9/26.
 */
public class SetRequest {
	private final String key;
	private final Object value;

	public SetRequest(String key, Object value) {
		this.key = key;
		this.value = value;
	}

	public String getKey() {
		return key;
	}

	public Object getValue() {
		return value;
	}

	@Override
	public String toString() {
		return "SetRequest{" +
				"key='" + key + '\'' +
				", value=" + value +
				'}';
	}
}

1.2.2 定义 Actor 收到消息后的行为

package com.shiw.ak;

import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.pf.ReceiveBuilder;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Sweeney on 2017/9/26.
 */
public class AkkademyDb extends AbstractActor {
	protected final LoggingAdapter log = Logging.getLogger(context().system(), this);
	protected final Map<String, Object> map = new HashMap<>();


	@Override
	public Receive createReceive() {
		return new ReceiveBuilder().match(SetRequest.class, message -> {
			log.info("Received Set request : {}", message);
			map.put(message.getKey(), message.getValue());
		}).matchAny(o -> log.info("Received unknown message : {}", o)).build();
	}
}

继承了 AbstractActor 的一般类就是一个 Actor。ReceiveBuilder 中的 match 方法有点像 case 语句,只不过 match 方法可以匹配 类的类型 ,正式一点说,这就是 模式匹配

1.2.3 使用单元测试验证代码

Akka 提供了一套测试工具,其中几乎包含了测试 Actor 代码需要的所有工具。

package com.shiw.ak;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.TestActorRef;
import org.junit.Assert;
import org.junit.Test;

/**
 * Created by Sweeney on 2017/9/26.
 */
public class AkkademyDbTest {
	ActorSystem system = ActorSystem.create();

	@Test
	public void test() {
		TestActorRef<AkkademyDb> aRef = TestActorRef.create(system, Props.create(AkkademyDb.class));
		aRef.tell(new SetRequest("key", "value"), ActorRef.noSender());
		AkkademyDb akkademyDB = aRef.underlyingActor();
		Assert.assertEquals(akkademyDB.map.get("key"), "value");
	}
}

  我们再 Actor 系统中创建 Actor 返回的是一个 ActorRef ,我们可以将消息发送至该 ActorRef。有了 Actor 系统和 ActorRef 后 Akka 就足以在 Actor 系统中创建这个简单的 Actor。
Actor 之间的交互是通过消息传递来进行的。使用 tell 将消息放到 Actor 的邮箱中。 通过 tell 的第二个参数(ActorRef.noSender())定义该消息并不需要任何响应对象。

  因为这里用的是 TestActorRef ,所以只有在 tell 调用请求处理完毕后,才会继续执行后面的代码。但要注意的是,这个例子并没有展示出 Actor API 的异步特性。而这并不是常见的用法,通常情况下 tell 是一个 异步操作 ,调用后会立即返回。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...