Akka工具(一)—Scheduler
小李:小胖,12月06日是我爸的生日,我怕那一天忘了,應該怎麼辦啊,你到時能提醒我一下嗎?非常感謝。
小胖:我也可能忘記啊,你再想想其它辦法吧!
小李:那怎麼辦啊,這麼重要的日子,我可不能忘記啊。
小胖:咦,你不是已經有手機了嗎?我聽說現在手機有日曆功能,有定時提醒功能,可以試試看。
小李:對啊,我們要物盡其用啊。
五分鐘後......
小李:好了,我剛剛測試了一下,果然可行,謝謝你啊,小胖,還是你聰明。
Scheduler
假如我們想在某個時刻或者某段時間處理某個任務,Akka中應該怎樣處理呢?既然這個問題我們都已經想到了,那麼Akka設計人員肯定想到了。別擔心,Akka已經提供了Scheduler物件幫助我們實現定時排程功能。
Scheduler物件不是通過new出來的,而是需要依賴ActorSystem得到,它在整個ActorSystem中是單例的,是唯一的。Scheduler提供兩種定時排程,用於不同的需求,總結如下:
排程API |
說明 |
scheduleOnce |
表示延遲一段時間後執行 |
scheduler |
表示延遲後定時重複執行,返回Cancellable物件,可用於取消該定時排程 |
引數分析:
Duration delay,延遲排程時間,可選值有day、hour、minute、second、millisecond等
ActorRef receiver,訊息處理類
Object message,定時傳送的訊息體
ExecutionContext executor,執行緒排程
ActorRef sender,接受receiver訊息返回值
關於這兩種不同的排程方式,我們使用程式碼來區分它們。
scheduleOnce
首先我們建立一個定時訊息處理類HandlerActor,
public class HandlerActor extends AbstractActor { private final LoggingAdapter logger = Logging.getLogger(getContext().getSystem(),this); @Override public Receive createReceive() { return receiveBuilder().match(String.class, logger::info) .matchAny((Object other) -> logger.info("其它未知訊息:" + other)) .build(); } }
現在,我們模擬100毫秒之後,給HandlerActor傳送訊息,如下:
ActorSystem system = ActorSystem.create("system");
ActorRef handlerActor = system.actorOf(Props.create(HandlerActor.class), "handlerActor");
//延遲100ms後,給handlerActor傳送當前時間訊息
system.scheduler().scheduleOnce(Duration.ofMillis(100),handlerActor, LocalDateTime.now(),system.dispatcher(),ActorRef.noSender());
當然,我們也可以這樣寫:
system.scheduler().scheduleOnce(Duration.ofMillis(100), new Runnable() {
@Override
public void run() {
handlerActor.tell(LocalDateTime.now(),ActorRef.noSender());
}
},system.dispatcher());
執行結果都一樣,如下:
[INFO] [11/24/2018 11:04:02.685] [system-akka.actor.default-dispatcher-5] [akka://system/user/handlerActor] 其它未知訊息:2018-11-24T11:04:02.683
scheduleOnce延遲100s後,只發送一次當前時間給handlerActor,後續不會再執行。
scheduler
我們還是沿用上述HandlerActor繼續處理訊息,現在我們想每隔1s後列印當前時間,修改如下:
public Receive createReceive() {
return receiveBuilder().matchEquals("time", s -> System.out.println(LocalDateTime.now())).build();
}
執行排程:
system.scheduler().schedule(Duration.ZERO, Duration.ofSeconds(1), handlerActor, "time", system.dispatcher(), ActorRef.noSender());
執行結果:
2018-11-24T11:37:00.864
2018-11-24T11:37:01.869
2018-11-24T11:37:02.879
2018-11-24T11:37:03.878
2018-11-24T11:37:04.878
2018-11-24T11:37:05.883
2018-11-24T11:37:06.861
大家可以看到,每隔1s就打印出了當前時間,當我們不在需要該輸出的時候,可以使用cancel方法停止該定時排程,cancel不會停止當前正在排程的任務,如下:
schedule.cancel();
大家可以使用sleep()方法模擬排程時長,自行除錯。
在Actor系統啟動後,會讀取akka.scheduler.implementation這個配置項,預設採用LightArrayRevolverScheduler,源配置(reference.conf中)如下:
# This setting selects the timer implementation which shall be loaded at
# system start-up.
# The class given here must implement the akka.actor.Scheduler interface
# and offer a public constructor which takes three arguments:
# 1) com.typesafe.config.Config
# 2) akka.event.LoggingAdapter
# 3) java.util.concurrent.ThreadFactory
implementation = akka.actor.LightArrayRevolverScheduler
這代表我們可以實現自己的定時排程,通過extends AbstractScheduler 就可以快速實現。