1. 程式人生 > >Akka工具(一)—Scheduler

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 就可以快速實現。