Akka笔记– DeathWatch – 7

当我们谈论Actor的生命周期时 ,我们看到可以通过各种方式(使用ActorSystem.stop或ActorContext.stop或发送PoisonPill来停止PoisonPill还有KillgracefulStop )。

无论Actor死于什么原因,在某些情况下,系统中的其他Actor都想知道它。 让我们举一个与数据库对话的Actor的简单例子-我们将其称为RepositoryActor 出于明显的原因,系统中几乎没有其他参与者会向此RepositoryActor发送消息。 这些“感兴趣的”演员希望继续eyewatch该演员,如果它出现故障。 现在,用Actor的术语称为DeathWatch watchunwatch watch的方法直观地是ActorContext.watchActorContext.unwatch 如果进行监视,则观察者将从停止的Actor收到Terminated消息,他们可以轻松地将其添加到其receive部分功能中。

与Supervision(下一个文章将在完成后将插入链接)不同,Supervision严格执行父子层次结构,任何Actor都可以watch ActorSystem中的任何其他Actor。

Akka笔记– DeathWatch – 7_第1张图片

让我们看一下代码。

QuoteRepositoryActor

  1. 我们的QueryRepositoryActor持有一堆quotes作为List,并在收到QuoteRepositoryRequest时提供随机QuoteRepositoryRequest
  2. 它跟踪接收到的消息数,如果接收到多于3条消息,则会使用PoisonPill杀死自己

这里没什么好看的。

package me.rerun.akkanotes.deathwatch

import akka.actor.{PoisonPill, Actor, ActorLogging, actorRef2Scala}  
import me.rerun.akkanotes.protocols.QuoteRepositoryProtocol._  
import scala.util.Random

class 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没什么QuoteRepositoryActor ,只是它创建了QuoteRepositoryActor并使用context.watch对其进行QuoteRepositoryActor

package me.rerun.akkanotes.deathwatch

import akka.actor.{Terminated, Props, Actor, ActorLogging}  
import me.rerun.akkanotes.protocols.TeacherProtocol.QuoteRequest  
import me.rerun.akkanotes.protocols.QuoteRepositoryProtocol.QuoteRepositoryRequest

class 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")
    }
  }
}

测试用例

这是有趣的一点。 坦白说,我从没想过可以对它们进行测试。 akka-testkit FTW。 我们将在这里分析三个测试用例:

1.确认收到Terminated消息的声明

QuoteRepositoryActor应该在收到第四条消息后向测试用例发送一条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.如果未关注/未关注,则声明未接收到Terminated消息

实际上,我们为了展示context.unwatch只是在做过多的事情。 如果我们删除testProbe.watchtestProbe.unwatch行,那么测试用例就可以正常工作。

"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并检查特定的日志消息以断言终止。

"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
        }
      }
    }

毫不奇怪, EventFilterpattern属性期望使用正则表达式模式。 pattern="""Child Actor .* Terminated"""应该与格式为Child Actor {Actor[akka://TestUniversityMessageSystem/user/$$d/quoteRepositoryActor#-1905987636]} Terminated的日志消息Child Actor {Actor[akka://TestUniversityMessageSystem/user/$$d/quoteRepositoryActor#-1905987636]} Terminated

Github

与往常一样,该代码可从github获得 。 注意deathwatch包。

翻译自: https://www.javacodegeeks.com/2014/11/akka-notes-deathwatch-7.html

你可能感兴趣的:(Akka笔记– DeathWatch – 7)