[转] AKKA简介
[From] https://blog.csdn.net/linuxarmsummary/article/details/79399602
Akka in JAVA(一)
AKKA简介
什么是AKKA
Akka是一个由Scala
编写的,能兼容Sacala
和JAVA
的,用于编写高可用和高伸缩性的Actor模型
框架.它基于了事件驱动的并发处理模式,性能非常的高,并且有很高的可用性.大大的简化了我们在应用系统中开发并发处理的过程.它在各个领域都有很好的表现.
使用AKKA的好处
就如上面简介中所说的,AKKA把并发操作的各种复杂的东西都统一的做了封装.我们主要关心的是业务逻辑的实现,只需要少量的关心Actor模型
的串联即可构建出高可用,高性能,高扩展的应用.
Akka for JAVA
由于AKKA是使用Scala
编写的,而Scala
是一种基于JVM的语言.因此JAVA
对AKKA的支持也是很不错的.Akka自身又是采用微内核的方式来实现的,这就意味着能很容易的在自己的项目中应用AKKA,只需要引入几个akka的Lib包即可.而官方直接就提供了Maven
库供我们在JAVA中使用AKKA.
这些AKKA的依赖包主要有:
- akka-actor:最核心的依赖包,里面实现了Actor模型的大部分东西
- akka-agent:代理/整合了Scala中的一些STM特性
- akka-camel:整合了Apache的Camel
- akka-cluster:akka集群依赖,封装了集群成员的管理和路由
- akka-kernel:akka的一个极简化的应用服务器,可以脱离项目单独运行.
- akka-osgi:对OSGI容器的支持,有akka的最基本的Bundle
- akka-remote:akka远程调用
- akka-slf4j:Akka的日志事件监听
- akka-testkit:Akka的各种测试工具
- akka-zeromq:整合ZeroMQ
其中最总要的就是akka-actor
,最简单的AKKA使用的话,只需要引入这个包就可以了.
Actor模型
什么是Actor
既然说AKKA是一个Actor模型
框架,那么就需要搞清楚什么是Actor模型
.Actor模型
是由Carl Hewitt
于上世纪70年代提出的,目的是为了解决分布式编程中的一系列问题而产生.
在Actor模型
中,一切都可以抽象为Actor.
而Actor是封装了状态和行为的对象,他们的唯一通讯方式就是交换消息,交换的消息放在接收方的邮箱(Inbox)里.也就是说Actor之间并不直接通信,而是通过了消息来相互沟通,每一个Actor都把它要做的事情都封装在了它的内部.
每一个Actor是可以有状态也可以是无状态的,理论上来讲,每一个Actor都拥有属于自己的轻量级线程,保护它不会被系统中的其他部分影响.因此,我们在编写Actor时,就不用担心并发的问题.
通过Actor能够简化锁以及线程管理,Actor具有以下的特性:
- 提供了一种高级的抽象,能够封装状态和操作.简化并发应用的开发.
- 提供了异步的非阻塞的/高性能的事件驱动模型
- 超级轻量级的线程事件处理能力.
要在JAVA中实现一个Actor
也非常的简单,直接继承akka.actor.UntypedActor
类,然后实现public void onReceive(Object message) throws Exception
方法即可.
Actor系统
光有一个一个独立的Actor显然是不行的.Akka中还有一个Actor System
.Actor System
统管了Actor
,是Actor的系统工厂或管理者,掌控了Actor的生命周期.
如上图所示,我们可以通过ActorSystem.create
来创建一个ActorSystem的实例.然后通过actorOf
等方法来获取ActorRef
对象.ActorRef
即为Actor Reference
.它是Actor的一个引用,主要的作用是发送消息给它表示的Actor.而Actor可以通过访问self()
或sender()
方法来获取到自身或消息发送者的Actor引用.通过引用发送消息.在Akka中,Actor之间永远都不能直接的通信,必须通过他们的代理ActorRef
建立通信.
Actor路径
为了实现一切事物都是Actor,为了能把一个复杂的事物划分的更细致.Akka引入了父子Actor.也就是Actor是有树形结构的关系的.这样的父子结构就能递归的把任何复杂的事物原子化.这也是Actor模型的精髓所在.这样做不仅使任务本身被清晰地划分出结构,而且最终的Actor也能按照他们明确的消息类型以及处理流程来进行解析.这样的递归结构使得消息能够在正确的层次进行处理.
为了能管理父子结构的Actor,Akka又引入了Actor Path
,也就是Actor路径.
Actor路径使用类似于URL的方式来描述一个Actor,Actor Path
在一个Actor System
中是唯一的.通过路径,可以很明确的看出某个Actor的父级关系是怎样的.
1
2
3
4
5
6
7
8
|
//本地Actor
"akka://my-sys/user/service-a/worker1"
//远程Actor
"akka.tcp://my-sys@host.example.com:2552/user/service-b"
//集群Actor服务
"cluster://my-cluster/service-c"
|
以上三种就是Akka中支持的Actor
路径. 每一个通过ActorSystem创建出来的Actor都会有一个这样的路径.也可以通过这个路径从ActorSystem中获取一个Actor
.
当我们创建一个ActorSystem的时候,AKKA会为该System默认的创建三个Actor,并处于不同的层次:
其中的root guardian
是所有Actor的父.
而User
Actor是所有用户创建的Actor的父.它的路径是/user
,通过system.actorOf()创建出来的Actor都算是用户的Actor,也都是这个Actor的子.System
Actor是所有系统创建的Actor的父.它的路径是/system
,主要的作用是提供了一系列的系统的功能.
当我们查找一个Actor的时候,可以使用ActorSystem.actorSelection()方法.并且可以使用绝对路径或者相对路径来获取.如果是相对路径,那么..
表示的是父Actor.比如:
1
2
3
|
ActorSelection selection = system.actorSelection("../brother");
ActorRef actor = selection.anchor();
selection.tell(xxx);
|
同时,也可以通过通配符来查询逻辑的Actor层级,比如:
1
2
|
ActorSelection selection = system.actorSelection("../*");
selection.tell(xxx);
|
这个就表示把消息发送给当前Actor之外的所有同级的Actor.
Hello AKKA Demo
原理讲了这么多,那么我们就来看一看一个最简单的Akka的例子吧.
这个是一个最简单的打招呼的例子,这个例子中,定义了招呼,打招呼的人两个对象或者说消息.然后定义了执行打招呼和打印招呼两个Actor.然后通过ActorSystem整合整个打招呼的过程.
Greet.java
1
2
3
4
5
6
7
8
|
/**
* 用于表示执行打招呼这个操作的消息
* @author SUN
* @version 1.0
* @Date 16/1/6 21:43
*/
public class Greet implements Serializable {
}
|
Greeting.java
1
2
3
4
5
6
7
8
9
10
11
12
|
/**
* 招呼体,里面有打的什么招呼
* @author SUN
* @version 1.0
* @Date 16/1/6 21:44
*/
public class Greeting implements Serializable {
public final String message;
public Greeting(String message) {
this.message = message;
}
}
|
WhoToGreet.java
1
2
3
4
5
6
7
8
9
10
11
12
|
/**
* 打招呼的人
* @author SUN
* @version 1.0
* @Date 16/1/6 21:41
*/
public class WhoToGreet implements Serializable {
public final String who;
public WhoToGreet(String who) {
this.who = who;
}
}
|
Greeter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/**
* 打招呼的Actor
* @author SUN
* @version 1.0
* @Date 16/1/6 21:40
*/
public class Greeter extends UntypedActor{
String greeting = "";
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof WhoToGreet)
greeting = "hello, " + ((WhoToGreet) message).who;
else if (message instanceof Greet)
// 发送招呼消息给发送消息给这个Actor的Actor
getSender().tell(new Greeting(greeting), getSelf());
else unhandled(message);
}
}
|
GreetPrinter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/**
* 打印招呼
* @author SUN
* @version 1.0
* @Date 16/1/6 21:45
*/
public class GreetPrinter extends UntypedActor{
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof Greeting)
System.out.println(((Greeting) message).message);
}
}
|
DemoMain.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/**
* @author SUN
* @version 1.0
* @Date 16/1/6 21:39
*/
public class DemoMain {
public static void main(String[] args) throws Exception {
final ActorSystem system = ActorSystem.create("helloakka");
// 创建一个到greeter Actor的管道
final ActorRef greeter = system.actorOf(Props.create(Greeter.class), "greeter");
// 创建邮箱
final Inbox inbox = Inbox.create(system);
// 先发第一个消息,消息类型为WhoToGreet
greeter.tell(new WhoToGreet("akka"), ActorRef.noSender());
// 真正的发送消息,消息体为Greet
inbox.send(greeter, new Greet());
// 等待5秒尝试接收Greeter返回的消息
Greeting greeting1 = (Greeting) inbox.receive(Duration.create(5, TimeUnit.SECONDS));
System.out.println("Greeting: " + greeting1.message);
// 发送第三个消息,修改名字
greeter.tell(new WhoToGreet("typesafe"), ActorRef.noSender());
// 发送第四个消息
inbox.send(greeter, new Greet());
// 等待5秒尝试接收Greeter返回的消息
Greeting greeting2 = (Greeting) inbox.receive(Duration.create(5, TimeUnit.SECONDS));
System.out.println("Greeting: " + greeting2.message);
// 新创建一个Actor的管道
ActorRef greetPrinter = system.actorOf(Props.create(GreetPrinter.class));
//使用schedule 每一秒发送一个Greet消息给 greeterActor,然后把greeterActor的消息返回给greetPrinterActor
system.scheduler().schedule(Duration.Zero(), Duration.create(1, TimeUnit.SECONDS), greeter, new Greet(), system.dispatcher(), greetPrinter);
//system.shutdown();
}
}
|
以上就是整个Demo的所有代码,并不多.接下来我们就分析一下这个程序.
首先是定义的几个消息.在Akka中传递的消息必须实现Serializable
接口.WhoToGreet
消息表示了打招呼的人,Greeting
表示了招呼的内容,而Greet
表示了打招呼这个动作.
接着就是两个最重要的Actor了.GreetPrinter
非常简单,接收到消息后,判断消息的类型,如果是Greeting
招呼内容,那么就直接打印消息到控制台.而Greeter
这个Actor稍微复杂点,它消费两种不同的消息,如果是WhoToGreet
,那么就把要打招呼的人记录到自己的上下文中,如果是Greet
,那么就构造出招呼的内容,并把消息反馈回sender.
最后,再来分析下DemoMain.
- 一来,先创建了一个
ActorSystem
, - 然后创建了一个
Greeter
Actor的实例,命名为greeter
. - 接着,为这个Actor,显示的创建了一个
邮箱
. - 而后,调用
greeter.tell(new WhoToGreet("akka"), ActorRef.noSender());
,表示给greeter这个Actor发送一个消息,消息的内容是WhoToGreet
,发送者是空.这就意味着在greeter这个Actor内部,调用sender是不能获取到发送者的.通过这个动作,就把消息限定为了单向的. - 再然后,通过
inbox.send(greeter, new Greet());
,使用邮箱显示的发送一个Greet消息给greeter.这是给Actor发送消息的另外一种方法,这种方法通常会有更高的自主性,能完成更多更复杂的操作.但是调用起来比直接使用ActorRef
来的复杂. Greeting greeting1 = (Greeting) inbox.receive(Duration.create(5, TimeUnit.SECONDS));
表示的就是尝试在5秒钟内,从Inbox
邮箱中获取到反馈消息.如果5秒内没有获取到,那么就抛出TimeoutException
异常. 由于我们在greeter这个Actor中有处理,接收到Greet
消息后,就构造一个Greeting
消息给sender
,因此这个地方是能够正确的获取到消息的反馈的.- 后面的操作都是一样的,就不再重复描述.
- 只有最后一个代码稍微有点不一样
system.scheduler().schedule(Duration.Zero(), Duration.create(1, TimeUnit.SECONDS), greeter, new Greet(), system.dispatcher(), greetPrinter);
,这个使用了ActorSystem
中的调度功能.每一秒钟给greeter这个Actor发送一个Greet
消息,并指定消息的发送者是greetPrinter
.这样每隔一秒钟,greeter就会收到Greet
消息,然后构造成Greeting
消息,又返回给GreetPrinter
这个Actor,这个Actor接收到消息后,打印出来.形成一个环流.
[转] AKKA简介的更多相关文章
- Akka 简介与入门
Akka 简介与入门 http://www.thinksaas.cn/group/topic/344095/ 参考官网 http://akka.io/ 开源代码 https://github.co ...
- Akka简介与Actor模型
Akka是一个构建在JVM上,基于Actor模型的的并发框架,为构建伸缩性强,有弹性的响应式并发应用提高更好的平台.本文主要是个人对Akka的学习和应用中的一些理解. Actor模型 Akka的核心就 ...
- Akka简介与Actor模型(一)
前言...... Akka是一个构建在JVM上,基于Actor模型的的并发框架,为构建伸缩性强,有弹性的响应式并发应用提高更好的平台.本文主要是个人对Akka的学习和应用中的一些理解. Actor模型 ...
- Spark如何使用Akka实现进程、节点通信的简明介绍
<深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...
- 分布式应用框架Akka快速入门
转自:http://blog.csdn.net/jmppok/article/details/17264495 本文结合网上一些资料,对他们进行整理,摘选和翻译而成,对Akka进行简要的说明.引用资料 ...
- SPARK如何使用AKKA实现进程、节点通信
SPARK如何使用AKKA实现进程.节点通信 <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 < ...
- Scala-Unit7-Scala并发编程模型AKKA
一.Akka简介 Akka时spark的底层通信框架,Hadoop的底层通信框架时rpc. 并发的程序编写很难,但是Akka解决了spark的这个问题. Akka构建在JVM平台上,是一种高并发.分布 ...
- Scala并发编程模型AKKA
一.并发编程模型AKKA Spark使用底层通信框架AKKA 分布式 master worker hadoop使用的是rpc 1)akka简介 写并发程序很难,AKKA解决spark这个问题. akk ...
- Akka系列(二):Akka中的Actor系统
前言......... Actor模型作为Akka中最核心的概念,所以Actor在Akka中的组织结构是至关重要,本文主要介绍Akka中Actor系统. 1.Actor系统 Actor作为一种封装状态 ...
随机推荐
- 彻底修改Eclipse的默认编码
引用各位前辈经验得到彻底修改eclipse默认编码的方法. 单在eclipse里设置编码方式非常复杂且容易遗漏,全部修改后,有些代码生成模板内的${encode}变量仍为原编码方案,经过查阅许多资料得 ...
- hdu 折线分割平面(递推)
题解: 首先我们考虑直线的情况: 当n=1时原来的1个平面被分割成了2个: 当n=2时原来的2个平面被分割成了4个: 当n=3时原来的4个平面被分割成了7个: 也就是说F(n)=F(n-1)+n且n= ...
- Smarty简介
Smarty是一个使用PHP写出来的模板引擎,是目前业界最著名的PHP模板引擎之一.它分离了逻辑代码和外在的内容,提供了一种易于管理和使用的方法,用来将原本与HTML代码混杂在一起PHP代码逻辑分离. ...
- Perl 学习笔记-子程序
1.定义子程序 使用sub关键字定义 ; 子程序名和标识符同要求, 但是有的特殊的可以用 &符号; 子程序是全局的, 不需要再使用前声明; 重名函数后者覆盖前者. sub roger{ ...
- PHP json_encode中文unicode转码问题
用PHP的json_encode来处理中文的时候, 中文都会被编码, 变成不可读的, 类似”\u***”的格式,如果想汉字不进行转码,这里提供三种方法 1.升级PHP,在PHP5.4, 这个问题终于得 ...
- [GO]结构体类型添加方法
package main import "fmt" type Person struct { name string sex byte age int } //带有接收者的函数叫方 ...
- mysql链接错误:2003 can't connect to mysql server on 10038
出现这个错误原因是端口号不是3306. 打开D:\Program Files\MySQL\MySQL Server 5.5 \my.ini文件,当然还有其他的.ini的文件: [client] ...
- Mybatis 多个Mapper
在实际应用中的,会有较多个mapper.如果每新建一个mapper,就向SqlMapConfig上加上对应的配置文件,会十分不便. 可以新建一个package,在其下面放置Mapper.java,同时 ...
- 关于提交表单时添加自定义值的方式:data中值可绑定function
表单提交时新增自定义值: $.ajaxForm(){ data:{aaa:"12"} } 但是这个是在初始化的时候就绑定进去的,所以值是固定的初始化时候的值,若想添加动态值,可以这 ...
- C#多线程学习(五) 多线程的自动管理(定时器)
Timer类:设置一个定时器,定时执行用户指定的函数. 定时器启动后,系统将自动建立一个新的线程,执行用户指定的函数. 初始化一个Timer对象: Timer timer ...