[翻译]AKKA笔记 - ACTOR MESSAGING - REQUEST AND RESPONSE -3
上次我们看Actor消息机制,我们看到开火-忘记型消息发出(意思是我们只要发个消息给Actor但是不期望有响应)。
技术上来讲, 我们发消息给Actors就是要它的副作用。 这就是这么设计的。除了不响应, 目标Actor当然也可以对消息做以下事情-
1.发送一个响应给发送者(在我们的case里,TeacherActor应该响应一首名言给StudentActor)
2.转发一个响应给其他的潜在的受众Actor,他们也可能响应/转发/产生副作用。Routers(路由)和Supervisors(监管)就是这个例子。(我们马上就能看到)
REQUEST & RESPONSE
这次,我们只关注第一点- 请求-响应的周期。
图片说明了我们这次在尝试归档做什么。为了简介,我没有在图中加入ActorSystem, Dispathcer或Mailboxes。
-
DriverApp发送一个InitSignal(初始化信号)消息给StudentActor.
-
StudentActor反馈给InitSignal消息并且发送一条QuoteRequest(格言请求)消息给TeacherActor。
-
TeacherActor,像我们在第一次看到的,响应一条QuoteResponse。
-
StudentActor只记录QuoteResponse(格言响应)到控制台/日志记录器。
我们可以写个testcase来验证。
让我们看下这4点的细节:
1. DRIVERAPP发送一个INITSIGNAL消息给STUDENTACTOR
至此,你可能已经猜出DriverApp会做什么。 就4件事:
1) 初始化ActorSystem
//Initialize the ActorSystem
val system = ActorSystem("UniversityMessageSystem")
- 创建TeacherActor
//create the teacher actor
val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
- 创建StudentActor
//create the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor
val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
你可以看到我把ActorRef的实例TeacherActor给StudentActor的构造函数,这样StudentActor可以使用ActorRef发消息给TeacherActor。还有另外的方式达到这个目的(如传入Props),但是这个方法对我们后面看到的Supervisors和Routers会方便点。我们也会即将看到child actors但现在还不是一个好方式 - Student创建Teacher听起来怪怪的吧?
最后,
4) DriverApp会发一个InitSignal给StudentActor, z何阳StudentActor就能开始发送QuoteRequest(格言请求)给TeacherActor。
//send a message to the Student Actor
studentRef ! InitSignal
这个DriverClass讲差不多了。 Thread.sleep和ActorSystem.shutdown在我们最后关闭ActorSystem的时候会在发消息时会等待几秒保证结束。
** DRIVERAPP.SCALA**
package me.rerun.akkanotes.messaging.requestresponse
import akka.actor.ActorSystem
import akka.actor.Props
import me.rerun.akkanotes.messaging.protocols.StudentProtocol._
import akka.actor.ActorRef
object DriverApp extends App {
//Initialize the ActorSystem
val system = ActorSystem("UniversityMessageSystem")
//construct the teacher actor
val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
//construct the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor
val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
//send a message to the Student Actor
studentRef ! InitSignal
//Let's wait for a couple of seconds before we shut down the system
Thread.sleep(2000)
//Shut down the ActorSystem.
system.shutdown()
}
2. STUDENTACTOR响应给INITSIGNAL消息并且发送一条QUOTEREQUEST消息给TEACHERACTOR###
以及
4. STUDENTACTOR从TEACHERACTOR接收到QUOTERESPONSE并且将日志记录到控制台/日志记录器###
为什么我把2和4条绑在一起?因为实在太简单了,要是拆开来你要恨死我。
所以,第二点 - StudentActor从DriverApp接到InitSignal消息并且发送QuoteRequest给TeacherActor。
def receive = {
case InitSignal=> {
teacherActorRef!QuoteRequest
}
...
...
就这么多!
第四点 - StudentActor记录从TeacherActor发来的日志消息。
就像我们承诺的:
case QuoteResponse(quoteString) => {
log.info ("Received QuoteResponse from Teacher")
log.info(s"Printing from Student Actor $quoteString")
}
我想你肯定同意现在这个看起来跟伪代码一样。
所以整个的StudentActor类看起来就是这样:
STUDENTACTOR.SCALA###
package me.rerun.akkanotes.messaging.requestresponse
import akka.actor.Actor
import akka.actor.ActorLogging
import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._
import me.rerun.akkanotes.messaging.protocols.StudentProtocol._
import akka.actor.Props
import akka.actor.ActorRef
class StudentActor (teacherActorRef:ActorRef) extends Actor with ActorLogging {
def receive = {
case InitSignal=> {
teacherActorRef!QuoteRequest
}
case QuoteResponse(quoteString) => {
log.info ("Received QuoteResponse from Teacher")
log.info(s"Printing from Student Actor $quoteString")
}
}
}
3. TEACHERACTOR响应QUOTERESPONSE消息###
这就跟我们在fire-n-forget 那里写的代码差不多。
TeacherActor接受一条QuoteRequest消息并且返回一条QuoteResponse。
TEACHERACTOR.SCALA###
package me.rerun.akkanotes.messaging.requestresponse
import scala.util.Random
import akka.actor.Actor
import akka.actor.ActorLogging
import akka.actor.actorRef2Scala
import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._
class TeacherActor extends Actor with ActorLogging {
val quotes = List(
"Moderation is for cowards",
"Anything worth doing is worth overdoing",
"The trouble is you think you have time",
"You never gonna know if you never even try")
def receive = {
case QuoteRequest => {
import util.Random
//Get a random Quote from the list and construct a response
val quoteResponse = QuoteResponse(quotes(Random.nextInt(quotes.size)))
//respond back to the Student who is the original sender of QuoteRequest
sender ! quoteResponse
}
}
}
测试用例TESTCASES###
现在,我们的测试用例会模拟DriverApp。由于StudentActor只是记录消息,我们无法对QuoteResponse消息进行断言,我们只断言消息在EventStream里出现(就像我们之前
说的)
所以,我们的测试用例就像这样:
"A student" must {
"log a QuoteResponse eventually when an InitSignal is sent to it" in {
import me.rerun.akkanotes.messaging.protocols.StudentProtocol._
val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
EventFilter.info (start="Printing from Student Actor", occurrences=1).intercept{
studentRef!InitSignal
}
}
}
源码###
整个工程可以在github下载。
下一步,我们会看到怎样使用Akka的schedulers并且用Kamon监控你的Akka应用。
文章来自微信平台「麦芽面包」,微信号「darkjune_think」。转载请注明。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?