原文地址 http://rerun.me/2014/10/06/akka-notes-actorsystem-in-progress/

2. SCHEDULE

图片描述

可以看到在ActorSystem的API有一个很有用的小方法叫scheduler,回返回一个SchedulerScheduler有很多 schedule方法让我们可以在Actor环境中做很多有趣的事情。

A. SCHEDULE SOMETHING TO EXECUTE ONCE

图片描述
拿我们的学生-老师的例子来说,假设我们的学生StudentActor要在收到InitSignal后5秒钟才发消息给老师,代码需要这样写:

class StudentDelayedActor (teacherActorRef:ActorRef) extends Actor with ActorLogging {

  def receive = {
    case InitSignal=> {
      import context.dispatcher
      context.system.scheduler.scheduleOnce(5 seconds, teacherActorRef, QuoteRequest)
      //teacherActorRef!QuoteRequest
    }
    ...
    ...
  }
}

测试用例
让我们写个测试用例来校验一下:

"A delayed student" must {

    "fire the QuoteRequest after 5 seconds when an InitSignal is sent to it" in {

      import me.rerun.akkanotes.messaging.protocols.StudentProtocol._

      val teacherRef = system.actorOf(Props[TeacherActor], "teacherActorDelayed")
      val studentRef = system.actorOf(Props(new StudentDelayedActor(teacherRef)), "studentDelayedActor")

      EventFilter.info (start="Printing from Student Actor", occurrences=1).intercept{
          studentRef!InitSignal
      }
    }

  }

增加EventFilter的超时时间
EventFilter等消息在EventStream中出现的时间默认为3秒钟。现在让我们把他增加到7秒来验证我们的测试用例。filter-leeway的配置项能帮我们做到这个。

class RequestResponseTest extends TestKit(ActorSystem("TestUniversityMessageSystem", ConfigFactory.parseString("""  
                                            akka{
                                              loggers = ["akka.testkit.TestEventListener"]
                                              test{
                                                  filter-leeway = 7s
                                              }
                                            }
                                    """)))  with WordSpecLike
  with MustMatchers
  with BeforeAndAfterAll 
  with ImplicitSender {
  ...
  ...

B. SCHEDULE SOMETHING TO EXECUTE REPEATEDLY

要想让东西能重复执行,你可以使用Scheduler的schedule方法。

一个经常被用来重载的schedule方法是用来发消息给Actor的方法。它接受4个参数:

第一次执行后要延迟多久开始初始化
执行子流程的频率
我们要发消息给哪个目标ActorRef
消息本身

case InitSignal=> {  
      import context.dispatcher
      context.system.scheduler.schedule(0 seconds, 5 seconds, teacherActorRef, QuoteRequest)      //teacherActorRef!QuoteRequest
    }

琐事

import context.dispatcher在这里非常重要。

schedule方法需要一个非常重要的隐藏参数 - ExecutionContext,在我们看了 schedule方法的实现后就会明白为什么了:

final def schedule(  
    initialDelay: FiniteDuration,
    interval: FiniteDuration,
    receiver: ActorRef,
    message: Any)(implicit executor: ExecutionContext,
                  sender: ActorRef = Actor.noSender): Cancellable =
    schedule(initialDelay, interval, new Runnable {      def run = {
        receiver ! message        if (receiver.isTerminated)          throw new SchedulerException("timer active for terminated actor")
      }
    })

schedule方法只是将tell包进了Runnable线程中,并且最终被我们传进的ExecutionContext执行。

为了让ExecutionContext在作用域中作为implicit(隐式类型),我们将上下文中的dispatcher声明为implicit。

ActorCell(上下文)中

/**
   * Returns the dispatcher (MessageDispatcher) that is used for this Actor.
   * Importing this member will place an implicit ExecutionContext in scope.
   */
  implicit def dispatcher: ExecutionContextExecutor

代码

跟往常一样,我们的代码可以在github 中下载到。
https://github.com/arunma/AkkaMessagingRequestResponse


文章来自微信平台「麦芽面包」
微信公众号「darkjune_think」
转载请注明。
如果觉得有趣,微信扫一扫关注公众号。
图片描述


祝坤荣
1k 声望1.5k 粉丝

科幻影迷,书虫,硬核玩家,译者


引用和评论

0 条评论