设为首页 加入收藏

TOP

使用Akka Actor和Java 8构建反应式应用(一)
2017-12-29 06:07:18 】 浏览:443
Tags:使用 Akka Actor Java 构建 反应 应用

尽管“反应式(reactive)”这个术语已经存在很长时间了,但是只有到最近它才被行业实际应用到系统设计之中,并得到了主流的采纳。在2014年Gartner就写到,过去非常流行的三层架构已经日薄西山。随着企业在推进现代化方面的努力,这一点已经越发明晰了,企业必须要重新思考他们十多年来构建应用的方式。


微服务席卷了软件行业,它所带来的冲击波正在从根本上动摇传统开发流程。我们看到软件设计范式发生了变化,项目管理的方法论也随之发生了演化。我们正在向新的应用设计和实现方式转变,它以前所未有的势头在IT系统中实现。即便微服务这个术语不是全新的概念,我们的行业也正在意识到它不仅仅是解耦RESTful端点和拆分单体应用,它真正的价值在于更好的资源利用效率以及面对不可预知工作负载时更强的扩展性。反应式宣言(Reactive Manifesto)的原则很快变成了微服务架构的圣经,因为它们本质上就是分布式的反应式应用。


为了保持用户的兴趣,应用必须要保持很高的响应性,同时,为了满足受众不断变化的需求和预期,应用必须要快速演化。用于构建应用的技术在不断地快速演进;科学在不断发展,持续涌现的新需求不能依赖于昨天的工具和方法论。Actor模型正在不断发展起来,它是一种构建应用的高效工具,能够充分发挥多核、内存以及集群环境所带来的强大处理能力。


Actor提供了一种简单却强大的模型,通过该模型设计和实现的应用可以分布式的,并且能够跨系统中所有的资源共享工作任务,这些资源可以从线程和核心级别一直到服务器集群和数据中心级别。它提供了一个高效的框架来构建应用,所构建出的应用具有较高的并发性,并且能够提升资源的利用率。另外很重要的一点,Actor模型还提供了定义良好的方式来优雅地处理错误和故障,确保应用的可靠性级别,它能够隔离问题,防止级联故障和长时间的宕机。


在过去,构建高并发的系统通常涉及到大量的装配和非常技术化的编程,它们都是非常难以掌握的。这些技术方面的挑战会抢占我们对系统核心业务功能的注意力,因为很大一部分的工作都集中在业务细节上,这需要花费很多的时间和精力用于搭建处理管道和功能装配。如果我们使用Actor来构建系统的话,就能在一个较高的抽象层级完成这些任务,因为处理管道和功能装配已经内置在了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依赖。


改变Actor的状态


就像通过移动设备收发短信一样,我们需要使用消息来调用Actor。与短信类似,Actor之间的消息也必须是不可变的。在使用Actor的时候,最重要的就是定义它所能接受的消息。(这种消息通常被称为协议,因为它定义了Actor之间的交互点。)Actor接收消息,然后以各种方式对其作出反应,它们可以发送其他的消息、修改自己的状态或行为、创建其他的Actor。


Actor的初始行为是通过实现receive()方法来定义的,在实现这个方法时,可以在默认的constructor. receive()中借助ReceiveBuilder匹配传入的消息并执行相关的行为。每条信息的行为通过一个Lambda表达式来定义。在下面的样例中,ReceiveBuilder使用了对接口方法“onMessage”的引用。onMessage方法增加了一个计数器(内部状态)并通过AbstractLoggingActor.log方法记录了一条info级别的日志信息。


Actor就绪之后,还需要启动它。这需要通过ActorSystem实现,它控制着Actor的生命周期。但是,我们首先需要提供一些关于如何启动这个Actor所需的额外信息。akka.actor.Props是一个配置对象,能够将上下文范围内的配置暴露给框架的各个地方。它用来创建我们的Actor,这个对象是不可变的,因此线程安全,完全可以共享。


return Props.create(Counter.class);


Props对象描述了Actor的构造器参数。将其封装到一个工厂函数中并放到Actor的构造器附近通常是一种好的实践。ActorSystem本身是在main方法中创建的。


ActorSystem (“sample1”)和它所包含的Actor(“counter”)都可以给定名称,这样便于在Actor层级结构中进行导航,这个话题稍后会进行讨论。现在,ActorRef可以发送一条消息给Actor,如样例所示:


counter.tell(new Counter.Message(), ActorRef.noSender());


在这里,使用两个参数定义了要发送的Message以及消息的发送者。(顾名思义,noSender表明在本例中,并没有使用发送者。)如果运行上述样例的话,我们就能得到预期的输出:


[01/10/2017 10:15:15.400] [sample1-akka.actor.default-dispatcher-4] [akka://sample1/user/counter] Increased counter 1


这是一个非常简单的样例,但是它提供了我们所需的线程安全性。发送给Actor的消息来源于不同的线程,这些消息屏蔽了并发的问题,因为Actor框架会进行消息的序列化处理。读者可以在线查看完整的样例。


读者可能已经注意到,我们的简单样例修改了Actor的状态,但是它并没有改变Actor行为,也没有发送消息给其他Actor。我们接下来考虑一个防盗报警系统,它可以通过密码来启用或禁用,它的传感器会探测活动。如果有人试图通过不正确的密码禁用告警的话,它就会发出声音。Actor能够响应三种消息,分别是通过密码(以负载的形式提供该值)进行禁用和启用的消息以及盗窃活动的消息。这三种消息都包含在了下面的协议中:


Actor有一个针对密码的预置属性,它也会传入到构造器中:


前面提到的akka.actor.Props配置对象也需要知道password属性,这样的话,才能在Actor系统启动的时候将其传递给实际的构造器。


针对每种可能的消息,Alarm还需要对应的行为。这些行为是AbstractActorreceive方法的实现。receive方法应该定义一系列的match语句(每个都是PartialFunction<Object, BoxedUnit>类型),它定义了Actor能够处

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Nodejs 做后台完整业务案例 下一篇Shell按行读取文件

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目