AKKA 与Actor 简单入门

不知道从什么时候响应式编程变得特别流行,我翻阅了很多资料,发现也没有一本书和一个版块能把这件事说清楚的,但是有一个网站发起了《响应式宣言》,里面大致罗列了设计响应式系统的设计准则,总结了下有以下四点:

1、灵敏性

只要有可能,系统就会及时响应。灵敏性是可用性和效用的基石,但还不止于此,灵敏性还意味着问题能够被更快地侦测到并得到有效地处理。灵敏的系统着眼于提供迅速和一致的响应时间,建立可靠的服务上限,因而它们可以交付一致的服务质量。这种一致的行为反过来又能简化出错处理,建立最终用户对系统的信心,并且促使他们与系统做进一步交互。

2、伸缩性

系统在变化的工作负载下保持灵敏。响应式系统能够对输入速率的变化做出反应,通过增加或减少资源分配的方式服务这些输入。这意味着系统在设计上不存在争用点或中心的瓶颈,进而让共享或复制的模块有能力应对,并可以把输入分发给这些模块。通过提供相关的实时性能测量数据,响应式系统既支持预测式量级扩展算法,也支持响应式的量级扩展算法。响应式系统可以在商业的硬件和软件平台上以经济实惠的方式实现可伸缩性。

3、容错性

系统在面临故障时也能保持灵敏度。可回复性不仅适用于高可用的关键任务系统——任何系统都可以具有这种属性,一个不具可回复性的系统一旦出现故障,就会变得不灵敏。可回复性可以通过复制,围控,隔离和委派等方式实现。在可回复性的系统中,故障被包含在每个组件中,各组件之间相互隔离,从而允许系统的某些部分出故障并且在不连累整个系统的前提下进行恢复。每个组件的恢复过程都可委派给另一个(外部的)组件来完成,并且在必要的位置可借助复制机制来确保系统的高可用性。这样一来,组件的客户就不必为处理组件自身的故障而承担压力。

4、事件驱动/消息驱动

响应式系统依赖异步的消息传递建立组件之间的界限,这一界限确保了松耦合,隔离,位置透明性等特性的实现,还提供了以消息的形式把故障委派出去的手段。利用显式的消息传递,可以通过建立和监视消息队列的方式实现负载管理、可伸缩性以及流程控制,还可以在必要时应用背压机制。位置透明的消息传递作为一种沟通手段,使得故障管理可以在相同的构造和语义下工作,无论实际的工作环境是跨集群的环境还是一个单独的主机。非阻塞沟通则允许消息接收者仅在激活的状态下消费资源,导向更少量的系统开销。

AKKA 与Actor 简单入门

大系统由较小的系统构成,因此势必依赖于这些构成要素的响应式属性。这意味着响应式系统需要应用一定的设计原则,使这些属性在所有不同等级的构成要素上都适用,进而使它们可组合。世界上最大的系统所依赖的体系结构,都是基于这些属性构建的,它们每天都在服务数十亿人的需求。因此只要满足响应式准则设计的系统,从理论上来说都不会有太大问题。

Actor Model

在1973年卡尔休伊特在一篇论文中首次提出了Actor,它是一种并行计算的模型 ;我们知道在使用Java语言进行并发编程时,需要特别关注共享的数据结构,线程间的资源竞争容易导致死锁等问题,而Actor模型便是要解决线程和锁带来的问题,Actor是一种基于事件(Event-Based)的轻量级线程,在使用Actor进行并发编程时只需要关注代码结构,而不需要过分关注数据结构,因此Actor最大限度地减少了数据的共享。

Actor 特别像一种生产者和消费者模式,它的流程如下图所示:

AKKA 与Actor 简单入门

两个Actor之间的通信是基于消息的,生产者的Actor将消息发送至邮箱,消费者的Actor从邮箱中消费消息。邮箱的底层实现是类似于FIFO队列。

一个Actor包含了三样东西,分别是行为、状态、消息。当一个Actor接收到消息的时候,它可以做三件事,分别是 :

1、创建另外一些Actors

2、向已知的Actors发消息

3、指定接收下一条消息时的行为

Actor是并行计算的最小颗粒:

•单个Actor的状态和行为只由接收到的消息驱动

•单个Actor串行地处理接收到的消息

• 单个Actor总是线程安全的 •

•大量Actors同时处在活跃状态,其行为是并行的

•并行是多个Actors的行为

那么使用Actor进行编程到底有什么好处呢? 它跟我们传统的面向对象编程有什么区别,我们以餐馆点餐为例来说明一下:

假设我们有三个Actor,分别代表客户、服务员、厨师

1.有一家餐馆,客人向服务员点单。

2.服务员将客人的菜品放在厨师的邮箱中 (厨师邮箱)

3.厨师获取这条消息,制作寿司(处理消息)

4.寿司准备好后,发一条消息给服务员邮箱(服务员邮箱,此时厨师可以给其他人做菜)

5.当服务员有空的时候,获取食物的消息,将其送到客人的餐桌(客户邮箱)

6.客人准备好的时候,就会吃寿司(处理消息)

我们从上面的例子中可以看出,每一步都是异步的,每个人都各司其职,如果采用传统的Java做法,可能每一步都是同步的,那么性能可想而知比较差,如果考虑到最坏的情况,每一步的状态都加锁的话,性能就更差了。从这个例子中可以看出Actor的威力了吗,它其实就源自我们的生活。

Actor实现

现在有很多种语言都实现了Actor,scala在Java的基础上添加了一些语法糖,功能不太强大,但是AKKA基于scala重写了scala actor,功能强大。ErLang语言在底层就实现了Actor,功能强大,大名鼎鼎的 rabbitmq就是基于erLang实现的。Go语言发展比较迅猛,它的实现方式通过模拟协诚的方式。


AKKA的 Actor实现

在AKKA中Actor 是非常轻量的计算单元,在单机单核本地上 有5000万/秒消息转发能力。1GB内存能生成 250万个Actors;

在AKKA中Actor 位置透明,本身既具分布能力它可以按地址创建和查找本地或远程节点,访问本地或远程节点仅在于地址不同, 可以跨节点迁移;

在AKKA中Actor 是按层级实现督导,Actor按树状组织成层级,父Actor监控子Actor的状态,可以在出状况时停止、重启、恢复。

使用AKKA

可以通过添加lib包或者通过maven的方式来添加AKKA的支持,个人比较推荐第二种方法,我们以2.5.11版本为例,首先在工程中添加如下maven配置

com.typesafe.akka

akka-actor_2.12

2.5.11

我们新建一个接收消息的Actor:

public class AkkaDb extends AbstractActor {protected final Map map = new ConcurrentHashMap<>();@Overridepublic PartialFunction receive() {return super.receive();}public Receive createReceive() {return ReceiveBuilder.create().match(SetRequest.class, message -> {map.put(message.getKey(), message.getValue());})}).matchAny(o -> sender().tell(new Status.Failure(new ClassNotFoundException()), self())).build();}}

这个actor做的事情特别简单,就是收到一条消息,将消息保存在map中,来模拟kv 数据库的用法,如果找不到,会报一个classNotFound error,AbstractActor是基于java 8的api,继承这个类之后,必须要实现createReceive 这个方法,match 是用来匹配消息的,在本例中匹配的是 SetRequest 这个类型的消息,当收到这条消息的时候,我们就把消息存入map;

public class SetRequest implements Serializable{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;}}

下面的例子演示了如何向这个Actor发送消息:

//创建actorSysytem,是所有actor的根对象ActorSystem actorSystem = ActorSystem.create();//actor的引用ActorRef akkadbRef = actorSystem.actorOf(Props.create(AkkaDb.class));SetRequest setRequest = new SetRequest("phone","159xxxxx");akkadbRef.tell(setRequest,ActorRef.noSender());

在Akka中创建Actor必须通过 ActorSystem,ActorSystem是Actor的根对象,在实际生产中建议使用全局单例的形式; 在本例子中我们通过actorSystem创建了 一个Actor的一个引用,通过这个引用就可以给actor发送消息了。我们发送了手机号是15xxx的一条消息,tell是AKKA发送消息的一种方式,是不需要等待回复结果的,第一个参数是发送消息的对象,第二个参数指定是由谁发送的,如果不想指定发送者,建议使用 noSender() 或者self()的形式。

今天就先简单介绍到这里,后续我们详细介绍AKKA发送消息的几种方式,例如:ask 类似于java 的future; tell 不需要回复结果的,性能最高; 如果有疑问的话可以留言给我,喜欢的话点击一下关注,我们会不定期每天推送一篇技术文章给你,感谢...


分享到:


相關文章: