[翻译]AKKA笔记 - DEATHWATCH -7

来源:互联网 发布:Windows支持的文件系统 编辑:程序博客网 时间:2024/06/06 00:29

当我们说Actor生命周期的时候,我们能看到Actor能被很多种方式停掉(用ActorSystem.stop或ActorContext.stop或发送一个PoisonPill - 也有一个killgracefulstop)。

无论Actor是怎么死的,有些情况一些系统中的其他actor想要知道。让我们举一个Actor与数据库交互的例子 - 我们叫它RepositoryActor。很明显,会有一些其他actor会向这个RepositoryActor发送消息。这些有“兴趣”的Actor很愿意留个eye或者看(watch)这个actor关闭时的消息。这个在Actor里面叫DeathWatch。这个用来watchunwatch的方法就是ActorContext.watchActorContext.unwatch。如果已经监视了,这些watcher会在Actor关闭时收到一个Terminated消息,并可以很舒服的加到他们的receive功能中。

不像Supervision有一个严格的父子继承关系,任何Actor都可以watch任何ActorSystem中的Actor。

这里写图片描述

让我们看下。

代码

QUOTEREPOSITORYACTOR

1.我们的QueryRepositoryActor格言查询Actor保存了一个quote的列表并且在收到一个QuoteRepositoryRequest时随机返回一条。

  1. 他记录了收到消息的个数,如果收到超过3个消息,他用PoisonPill把自己杀掉

这没啥神奇的。

package me.rerun.akkanotes.deathwatchimport akka.actor.{PoisonPill, Actor, ActorLogging, actorRef2Scala}  import me.rerun.akkanotes.protocols.QuoteRepositoryProtocol._  import scala.util.Randomclass QuoteRepositoryActor() 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")  var repoRequestCount:Int=1  def receive = {    case QuoteRepositoryRequest => {      if (repoRequestCount>3){        self!PoisonPill      }      else {        //Get a random Quote from the list and construct a response        val quoteResponse = QuoteRepositoryResponse(quotes(Random.nextInt(quotes.size)))        log.info(s"QuoteRequest received in QuoteRepositoryActor. Sending response to Teacher Actor $quoteResponse")        repoRequestCount=repoRequestCount+1        sender ! quoteResponse      }    }  }}

TEACHERACTORWATCHER

一样的,TeacherActorWatcher也没啥神奇的,除了他创建了一个QuoteRepositoryActor并且用context.watch观察。

package me.rerun.akkanotes.deathwatchimport akka.actor.{Terminated, Props, Actor, ActorLogging}  import me.rerun.akkanotes.protocols.TeacherProtocol.QuoteRequest  import me.rerun.akkanotes.protocols.QuoteRepositoryProtocol.QuoteRepositoryRequestclass TeacherActorWatcher extends Actor with ActorLogging {  val quoteRepositoryActor=context.actorOf(Props[QuoteRepositoryActor], "quoteRepositoryActor")  context.watch(quoteRepositoryActor)  def receive = {    case QuoteRequest => {      quoteRepositoryActor ! QuoteRepositoryRequest    }    case Terminated(terminatedActorRef)=>{      log.error(s"Child Actor {$terminatedActorRef} Terminated")    }  }}

测试CASE

这里会有点意思。我从来没想过这个可以被测试。akka-testkit。我们会分析下这三个测试CASE:

1. 断言如果观察到已经收到Terminated消息

QuoteRepositoryActor应该在收到第四条消息时给测试case发送一条Terminated消息。前三条应该是可以的。

"A QuoteRepositoryActor" must {    ...    ...    ...    "send back a termination message to the watcher on 4th message" in {      val quoteRepository=TestActorRef[QuoteRepositoryActor]      val testProbe=TestProbe()      testProbe.watch(quoteRepository) //Let's watch the Actor      within (1000 millis) {        var receivedQuotes = List[String]()        (1 to 3).foreach(_ => quoteRepository ! QuoteRepositoryRequest)        receiveWhile() {          case QuoteRepositoryResponse(quoteString) => {            receivedQuotes = receivedQuotes :+ quoteString          }        }        receivedQuotes.size must be (3)        println(s"receiveCount ${receivedQuotes.size}")        //4th message        quoteRepository!QuoteRepositoryRequest        testProbe.expectTerminated(quoteRepository)  //Expect a Terminated Message      }    }

2.如果没有观察(watched/unwatched)到则断言没收到Terminated消息

事实上,我们做这个只是演示下context.unwatch。如果我们移掉testProbe.watch和testProbe.unwatch这行,则测试case会运行的很正常。

    "not send back a termination message on 4th message if not watched" in {      val quoteRepository=TestActorRef[QuoteRepositoryActor]      val testProbe=TestProbe()      testProbe.watch(quoteRepository) //watching      within (1000 millis) {        var receivedQuotes = List[String]()        (1 to 3).foreach(_ => quoteRepository ! QuoteRepositoryRequest)        receiveWhile() {          case QuoteRepositoryResponse(quoteString) => {            receivedQuotes = receivedQuotes :+ quoteString          }        }        testProbe.unwatch(quoteRepository) //not watching anymore        receivedQuotes.size must be (3)        println(s"receiveCount ${receivedQuotes.size}")        //4th message        quoteRepository!QuoteRepositoryRequest        testProbe.expectNoMsg() //Not Watching. No Terminated Message      }    }

3. 在TeacherActorWatcher中断言收到了Terminated消息

我们订阅了EventStream并通过检查一个特殊的日志消息来断言termination。

   "end back a termination message to the watcher on 4th message to the TeacherActor" in {      //This just subscribes to the EventFilter for messages. We have asserted all that we need against the QuoteRepositoryActor in the previous testcase      val teacherActor=TestActorRef[TeacherActorWatcher]      within (1000 millis) {        (1 to 3).foreach (_=>teacherActor!QuoteRequest) //this sends a message to the QuoteRepositoryActor        EventFilter.error (pattern="""Child Actor .* Terminated""", occurrences = 1).intercept{          teacherActor!QuoteRequest //Send the dangerous 4th message        }      }    }

EventFilter中的pattern属性,没啥奇怪的,需要一个正则表达式。正则pattern=”“”Child Actor .* Terminated”“”用来匹配一条格式是Child Actor {Actor[akka://TestUniversityMessageSystem/user/$$d/quoteRepositoryActor#-1905987636]} Terminated日志信息。

Github

与往常一样,代码在github。看下deathwatch的包。


文章来自微信平台「麦芽面包」(微信扫描二维码关注)。未经允许,禁止转载。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 魔域手游怎么改密码忘了怎么办 魔域装备注灵怎么办 lol外服账号忘记了怎么办 美服lol下载慢怎么办 台服天堂2延迟怎么办 梦幻专用瑞兽没有泰山怎么办 冲错了游戏点券怎么办 新手玩联盟很菜怎么办 cf玩一会儿卡退怎么办 魔域先锋区封号了怎么办 吃了减肥药头疼怎么办 冬天没用完的霜怎么办 手表带起来大了怎么办 碰到舞警打人该怎么办 合租者偷了东西却没有证据怎么办 钥匙锁在房间了怎么办 家里门钥匙丢了怎么办 合租朝北晒衣服怎么办 卧室门钥匙丢了怎么办 邻居在我家防盗窗上凉被子怎么办 有钥匙打不开门怎么办 白色腈纶衣服洗完发黄怎么办 在部队有人整你怎么办 老公掉粪坑了你怎么办图片 好久没跑步腿疼怎么办 跑1000米要5分钟怎么办 孩子眼睛近视加散光怎么办 在部队混的差怎么办 2岁宝宝走路踮脚怎么办 宝宝走路膝盖弯曲不直怎么办 做了蛙跳大腿疼怎么办 跳完蛙跳腿疼怎么办 腿受凉了疼怎么办偏方 鸭子步蛙跳后腿疼怎么办 戴墨镜鼻子会红怎么办 校服黑色裙子染色了怎么办 新警培训时怀孕怎么办? 大学生欠了网贷怎么办? 车侧面底盘被刮怎么办 军人家属被打没人处理怎么办 孩子字写的难看怎么办