akka设计模式系列-基础模式
本文介绍akka的基本使用方法,由于属于基础功能,想不出一个很高大上的名称,此处就以基础模式命名。下文会介绍actor的使用方法,及其优劣点。
class SimpleActor(name:String) extends Actor {
private def doWork(message:SayHello):Unit = {
println(s"$name 收到 ${message.from.path.name} 的消息 [$message] ,工作进行中... 当前线程号 ${Thread.currentThread().getId}")
}
override def receive: Receive = {
case msg @ SayHello(from,message) =>
doWork(msg)
val returnMsg = HelloSaid(s"嗨 ${from.path.name} ,${self.path.name} 收到了 $message 消息")
println(s"$name 工作结束,准备返回消息[${returnMsg.message}]")
}
}
object SimpleBasicPattern {
def main(args: Array[String]): Unit = {
val system = ActorSystem("BasicPattern",ConfigFactory.load())
val person1 = system.actorOf(Props(new SimpleActor("person1")),"personActor1")
println(s"Main thread Id ${Thread.currentThread().getId}")
person1 ! SayHello(person1,"Hello World 1")
person1 ! SayHello(person1,"Hello World 2")
}
}
输出: Main thread Id 1
person1 收到 personActor1 的消息 [SayHello(Actor[akka://BasicPattern/user/personActor1#1662593548],Hello World 1)] ,工作进行中... 当前线程号 13
person1 工作结束,准备返回消息[嗨 personActor1 ,personActor1 收到了 Hello World 1 消息]
person1 收到 personActor1 的消息 [SayHello(Actor[akka://BasicPattern/user/personActor1#1662593548],Hello World 2)] ,工作进行中... 当前线程号 13
person1 工作结束,准备返回消息[嗨 personActor1 ,personActor1 收到了 Hello World 2 消息]
如上图,我设计了一个简单的actor:HelloWroldActor。它有两个方法,其中receive是收到消息之后处理消息的入口函数,定义了对消息的处理方式。收到SayHello之后,调用doWork同步处理消息
我们可以跟上一篇博客进行对比,此处给person1发送了一条SayHello消息,在OOP中是直接调用函数,此处使用 ! 函数发送消息;person1收到消息后,同步调用doWork处理消息。这是最基本的actor使用方式:通过 ! 发消息给actor。从输出中可以看到,主线程和doWork所在线程是不同的线程。
这是基础模式的最基本形式,给actor发送消息,actor对消息进行响应,发送和响应是异步的,同一个actor对所有的消息都是按照邮箱队列的顺序,串行调用的。下面是基础模式的另外一种高级形式。
class HelloWorldActor(other:ActorRef,name:String) extends Actor {
private def doWork(message:String):HelloSaid = {
println(s"$name 收到 ${other.path.name} 的消息 [$message] ,工作进行中...")
HelloSaid("这是处理后返回的消息")
}
override def receive: Receive = {
case DoWork(message) =>
println(s"嗨 ${other.path.name} ,我正在为你工作")
val returnMsg = doWork(message)
other ! WorkDone(returnMsg.message)
case WorkDone(message) =>
println(s"$name 收到了 ${sender().path.name} 的回复消息:[$message]")
}
}
object BasicPattern {
def main(args: Array[String]): Unit = {
val system = ActorSystem("BasicPattern",ConfigFactory.load())
val person1 = system.actorOf(Props(new HelloWorldActor(null,"person1")),"personActor1")
val person2 = system.actorOf(Props(new HelloWorldActor(person1,"person2")),"personActor2")
person2 ! DoWork("Hello World")
}
}
输出: 嗨 personActor1 ,我正在为你工作
person2 收到 personActor1 的消息 [Hello World] ,工作进行中...
person1 收到了 personActor2 的回复消息:[嗨 personActor1 工作已完成,这是返回消息 HelloSaid(这是处理后返回的消息)]
在上面的模式中,我们首先给person2发送了开始工作的消息,person2收到消息后,开始为person1工作:调用doWork进行计算。计算结束后把消息发送给了person1,person1收到workDone的消息后,将结果打印了出来。这个例子稍微复杂点,涉及到了两个actor的通信。但这仍然是一种简单的形式,因为person1的actorRef引用是通过构造函数传递给person2的,这样person2就只能为person1工作。这非常不方便,因为actor创建的时候不一定能知道另外一个actor的地址。那么下面又是一种高级形式:
class HelloActor(name:String) extends Actor {
private def doWork(message:String,forActor:ActorRef):HelloSaid = {
println(s"$name 收到 ${forActor.path.name} 的消息 [$message] ,工作进行中...")
HelloSaid("这是处理后返回的消息")
}
override def receive: Receive = {
case DoWorkFor(message,forActor) =>
println(s"嗨 ${forActor.path.name} ,我正在为你工作")
val returnMsg = doWork(message,forActor)
forActor ! WorkDone(returnMsg.message)
case WorkDone(message) =>
println(s"$name 收到了 ${sender().path.name} 的回复消息:[$message]")
}
}
object BasicPattern3 {
def main(args: Array[String]): Unit = {
val system = ActorSystem("BasicPattern2",ConfigFactory.load())
val person1 = system.actorOf(Props(new HelloActor("person1")),"personActor1")
val person2 = system.actorOf(Props(new HelloActor("person2")),"personActor2")
person2 ! DoWorkFor("Hello World",person1)
}
}
输出:
嗨 personActor1 ,我正在为你工作
person2 收到 personActor1 的消息 [Hello World] ,工作进行中...
person1 收到了 personActor2 的回复消息:[这是处理后返回的消息]
在上面的图中,我们把person1的actorRef通过消息的形式发送给了person2,这样person2就能为不同的person工作了,因为工作的对象是通过消息传递的。
通过上面3个例子,我们可以看到,只能通过给actor发送消息与actor通信,调用其对应的函数,函数的返回结果也只能异步的发送给调用方。而在OOP中调用另一个对象的函数,看起来比这个简单多了,获取函数处理结果也非常简单。但读者要仔细思考这两者的区别,actor的通信全都是异步的。意味着person2给person1发送消息之后,可以立即进行其他的处理,而不需要等待person1的应答,即person1和person2功能做到了完全解耦。
BasicPattern2和BasicPattern3的区别是调用方获取方式的不同,其实还有另外一种形式:
class Master(workerPath:String) extends Actor{
override def receive: Receive = {
case DoWork(message) =>
println(s"master 收到 doWork消息:$message")
val worker = context.actorSelection( s"/user/$workerPath")
worker ! DoWorkFor(message,self)
case WorkDone(message) =>
println(s"master 收到 ${sender().path.name} 的返回消息 $message")
}
}
class Worker extends Actor{
private def doWork(message:String):String = {
println(s"worker 收到了消息 $message")
"这里是worker返回消息"
}
override def receive: Receive = {
case DoWorkFor(message,forActor) =>
val result = doWork(message)
forActor ! WorkDone(result) }
}
object BasicPattern4 {
def main(args: Array[String]): Unit = {
val system = ActorSystem("BasicPattern2",ConfigFactory.load())
system.actorOf(Props(new Worker()),"workerActor")
val master = system.actorOf(Props(new Master("workerActor")),"masterWorker")
master ! DoWork("Hello World")
}
}
输出 master 收到 doWork消息:Hello World
worker 收到了消息 Hello World
master 收到 workerActor 的返回消息 这里是worker返回消息
在这个形式中,master通过worker的actorPath,用actorSelection查询了worker的地址,然后发送消息给它。与BasicPattern3不同的是,master不需要知道worker的邮箱地址,它只需要知道worker的actorPath就可以发消息了。然后master和worker就可以按照前面的pattern互通消息了。
请注意DoWork、WorkDone、DoWork这三个消息的处理完全是异步的,没有任何直接的关系。
上面的4个例子我都将其认定为基础模式,因为这都是Akka的基础功能,没有涉及太高深的技术,也是在学习Akka的初期最容易理解的模式。虽然简单,还是有很多值得学习的地方的。在下一篇博客中,我们会针对BasicPattern4进行优化,讲解另外MasterWorkerBackend模式,这种模式比较复杂,希望读者深刻理解本博文的4个例子,再阅读后续文章。
akka设计模式系列-基础模式的更多相关文章
- akka设计模式系列-While模式
While模式严格来说是while循环在Akka中的合理实现.while是开发过程中经常用到的语句之一,也是绝大部分编程语言都支持的语法.但while语句是一个循环,如果循环条件没有达到会一直执行wh ...
- akka设计模式系列-Chain模式
链式调用在很多框架和系统中经常存在,算不得上是我自己总结的设计模式,此处只是简单介绍在Akka中的两种实现方式.我在这边博客中简化了链式调用的场景,简化后也更符合Akka的设计哲学. trait Ch ...
- akka设计模式系列-Backend模式
上一节我们介绍了Akka使用的基本模式,简单点来说就是,发消息给actor,处理结束后返回消息.但这种模式有个缺陷,就是一旦某个消息处理的比较慢,就会阻塞后面所有消息的处理.那么有没有方法规避这种阻塞 ...
- akka设计模式系列-Aggregate模式
所谓的Aggregate模式,其实就是聚合模式,跟masterWorker模式有点类似,但其出发点不同.masterWorker模式是指master向worker发送命令,worker完成某种业务逻辑 ...
- akka设计模式系列-慎用ask
慎用ask应该是Akka设计的一个准则,很多时候我们应该禁用ask.之所以单独把ask拎出来作为一篇博文,主要是akka的初学者往往对ask的使用比较疑惑. "Using ask will ...
- akka设计模式系列-消息模型(续)
在之前的akka设计模式系列-消息模型中,我们介绍了akka的消息设计方案,但随着实践的深入,发现了一些问题,这里重新梳理一下设计方法,避免之前的错误.不当的观点给大家带来误解. 命令和事件 我们仍然 ...
- akka设计模式系列
由于本人爱好Scala,顺便也就爱好Akka,但目前网上对Akka的介绍大多都是概念上或技术方向上的介绍,基本没有Akka设计模式或者Actor模型设计模式的资料.这对于Akka的普及非常不利,因为即 ...
- PHP设计模式系列 - 外观模式
外观模式 通过在必需的逻辑和方法的集合前创建简单的外观接口,外观设计模式隐藏了调用对象的复杂性. 外观设计模式和建造者模式非常相似,建造者模式一般是简化对象的调用的复杂性,外观模式一般是简化含有很多逻 ...
- c++设计模式系列----builder模式
看了好几处关于builder模式的书和博客,总感觉不是很清楚,感觉不少书上的说的也不是很准确.最后还是看回圣经<设计模式>.看了好久终于感觉明白了一点了. 意图: builder模式提出的 ...
随机推荐
- UVA - 1615 Highway(贪心-区间选点问题)
题目: 给定平面上n(n≤105)个点和一个值D,要求在x轴上选出尽量少的点,使得对于给定的每个点,都有一个选出的点离它的欧几里得距离不超过D. 思路: 先自己造区间,然后贪心选点就可以了.之前做过一 ...
- zabbix部署-版本3.2.6
172.18.237.14:一台主机上安装LAMP环境以及zabbix_server.zabbix_agentd 一.安装zibbix-server 1.环境要求 yum install mysql- ...
- Laravel5.5 综合使用
使用 Laravel5.5 开发一个自动交割的项目,把使用到的开源扩展包及特性整理起来,以供后续使用. 一.安装IDE提示工具 Laravel IDE Helper 是一个极其好用的代码提示及补全工具 ...
- go 语言学习指南(一)
参考资料: http://www.runoob.com/go/go-tutorial.html
- IIS部署网站只有首页能访问,其他链接失效/运行.net+Access网站-可能原因:IIS未启用32位应用程序模式
在64位的机子上IIS运行32位的.NET程序 由于64位操作系统不支持Microsoft OLE DB Provider for Jet驱动程 也不支持更早的Microsoft Access Dri ...
- 如何创建新用户和授予MySQL中的权限
原创官网http://www.howtoing.com/how-to-create-a-new-user-and-grant-permissions-in-mysql/ 关于MySQL MySQL是一 ...
- POJ 2217 Secretary
Secretary Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: ...
- 写给对<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">迷惑不解的小伙伴
1.X-UA-Compatible X-UA-Compatible是自从IE8新加的一个设置,对于IE8以下的浏览器是不识别的. 通过在meta中设置X-UA-Compatible的值,可以指定网页的 ...
- 布局(codevs 1242)
题目描述 Description 当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些.FJ有N(2<=N<=1000)头奶牛,编号从1到N,沿一条直线站着等候喂食.奶牛排在队伍中的顺序和它们 ...
- bitset优化FLOYD HDU 3275
Each of Farmer John's N cows (1 ≤ N ≤ 1,000) produces milk at a different positive rate, and FJ woul ...