草庐IT

Akka定时任务schedule()方法

Maverick_曲流觞 2023-04-29 原文

Akka定时任务schedule()方法

文章目录

什么是Akka定时任务schedule()方法?

Akka定时任务schedule()方法是一种在Akka actor系统中管理周期性执行任务的方式。它可以让我们在指定的时间点或时间间隔,向actor发送消息或执行函数。它返回一个Cancellable对象,我们可以调用它的cancel()方法来取消定时任务的执行。

Akka定时任务schedule()方法是基于一个哈希轮定时器(Hashed Wheel Timer)实现的,它是一种高效的数据结构和算法,用于处理大量的定时触发事件,如actor接收超时、Future超时、断路器等。它不会精确地在指定的时间点执行任务,而是在每个滴答(tick)时,执行所有已经到期的任务。我们可以通过配置属性akka.scheduler.tick-duration来修改Akka定时任务schedule()方法的精度。

如何使用Akka定时任务schedule()方法?

如何在actor外部获取Scheduler对象

要使用Akka定时任务schedule()方法,我们需要先获取一个Scheduler对象,它是每个ActorSystem唯一的,并且用于内部调度各种事件。我们可以通过调用ActorSystem的scheduler方法来获取它:

// 获取一个ActorSystem对象
val system = ActorSystem("akka-scheduler-system")
// 获取一个Scheduler对象
val scheduler = system.scheduler

然后,我们需要提供一个隐式的ExecutionContext对象,用于执行定时任务。我们可以使用ActorSystem自带的dispatcher作为ExecutionContext:

// 导入ActorSystem自带的dispatcher作为ExecutionContext
import system.dispatcher

为什么需要提供一个隐式的ExecutionContext对象,用于执行定时任务?

ExecutionContext是一个表示执行上下文的特质,它可以异步地执行程序逻辑,通常但不一定是在一个线程池上。它类似于Java的Executor接口,它有两个抽象方法:execute和reportFailureexecute方法用于执行一个Runnable对象,reportFailure方法用于报告一个异步计算失败的原因。

当我们使用Akka定时任务schedule()方法创建定时任务时,我们需要提供一个ExecutionContext对象,用于执行我们传入的函数或Runnable对象。这样,我们可以控制定时任务的执行策略,例如使用哪个线程池,如何处理异常等。我们可以使用ActorSystem自带的dispatcher作为ExecutionContext,也可以自定义一个ExecutionContext。

为了方便使用,我们通常会把ExecutionContext对象作为一个隐式参数传入schedule()方法。这样,我们就不需要显式地指定ExecutionContext对象,只需要在作用域内导入或定义一个隐式的ExecutionContext对象即可。例如:

// 导入ActorSystem自带的dispatcher作为隐式的ExecutionContext
import system.dispatcher
// 创建一个定时任务,不需要显式地传入ExecutionContext
scheduler.schedule(100.millis, 1.second)(() => greeter ! greet)

或者

// 定义一个自定义的ExecutionContext作为隐式的ExecutionContext
implicit val ec: ExecutionContext = ...
// 创建一个定时任务,不需要显式地传入ExecutionContext
scheduler.schedule(100.millis, 1.second)(() => greeter ! greet)

这样,我们就可以简化代码的书写和理解,同时保留了定时任务执行策略的灵活性。

如果我们不想使用隐式参数的方式来传入ExecutionContext对象,我们可以显式地指定ExecutionContext对象,只需要在schedule()方法的最后一个参数列表中加上ExecutionContext对象即可。例如:

// 定义一个自定义的ExecutionContext对象
val ec: ExecutionContext = ...
// 创建一个定时任务,显式地传入ExecutionContext对象
scheduler.schedule(100.millis, 1.second)(() => greeter ! greet)(ec)

这样,我们就可以显式地指定ExecutionContext对象,而不依赖于作用域内的隐式参数。这种写法可能会更清晰和安全,但也会增加代码的冗余和复杂度。因此,我们应该根据具体的场景和需求来选择是否使用隐式或显式的方式来传入ExecutionContext对象。

如何在actor内部获取Scheduler对象

context是一个ActorContext对象,它表示一个actor的上下文信息,包括它的self引用,它的子actor,它的监督策略等。context也有一个system属性,它是一个ActorSystem对象,表示这个actor所属的actor系统。因此,context.system.scheduler.schedule实际上就是调用这个actor所属的actor系统的scheduler方法来创建定时任务。

这种写法通常出现在actor内部,当我们想要在actor内部创建定时任务时,我们可以使用context.system.scheduler.schedule来获取Scheduler对象。例如:

// 在actor内部创建一个定时任务,向自己发送HeartBeat消息
context.system.scheduler.schedule(100.millis, 1.second)(() => self ! HeartBeat)

这样,我们就不需要显式地获取或传入ActorSystem对象,只需要使用context.system即可。

schedule()方法的格式

接下来,我们可以使用Scheduler对象的schedule()方法来创建单次执行或重复执行的定时任务。这个方法有两种重载形式:

  • def schedule(initialDelay: FiniteDuration, interval: FiniteDuration)(f: ⇒ Unit): Cancellable:这个方法用于创建重复执行的定时任务,它接受三个参数:初始延迟(initialDelay),表示第一次执行任务的延迟时间;间隔时间(interval),表示每次执行任务之间的时间间隔;函数(f),表示要执行的任务。它返回一个Cancellable对象,用于取消定时任务。
  • def schedule(initialDelay: FiniteDuration, interval: FiniteDuration, receiver: ActorRef, message: Any): Cancellable:这个方法用于创建重复向actor发送消息的定时任务,它接受四个参数:初始延迟(initialDelay),表示第一次发送消息的延迟时间;间隔时间(interval),表示每次发送消息之间的时间间隔;接收者(receiver),表示要发送消息给哪个actor;消息(message),表示要发送什么消息。它返回一个Cancellable对象,用于取消定时任务。

如果我们只想创建单次执行或单次发送消息的定时任务,我们可以将间隔时间设置为Duration.Zero。

Akka定时任务schedule()方法有哪些类型的延迟?

Akka定时任务schedule()方法有两种类型的延迟:固定延迟(fixed-delay)和固定频率(fixed-rate)。

固定延迟

固定延迟指的是两次连续的任务执行之间的延迟时间总是至少等于给定的间隔时间。下一次任务执行的时间只有在当前任务执行完成后才会计算。如果当前任务是一个长时间运行的任务,那么下一次任务执行会延迟。因此,两次连续的任务执行之间的时间差可能不是恒定的。

例如:

// 创建一个重复向actor发送消息的定时任务,初始延迟为100毫秒,间隔时间为1秒
scheduler.schedule(100.millis, 1.second, greeter, greet)

或者

// 创建一个重复执行函数的定时任务,初始延迟为10毫秒,间隔时间为250毫秒
scheduler.schedule(10.millis, 250.millis)(() => greeter ! greet)

注意,schedule()方法只能创建固定延迟的定时任务,如果我们想创建固定频率的定时任务,我们需要使用新的scheduleAtFixedRate()方法。

从Akka 2.6版本开始,schedule()方法已经被弃用,Akka建议我们使用scheduleWithFixedDelay()方法来创建固定延迟的定时任务。所以,我们可以重写前面的例子,使用scheduleWithFixedDelay()方法:

// 创建一个重复向actor发送消息的定时任务,初始延迟为100毫秒,间隔时间为1秒
scheduler.scheduleWithFixedDelay(100.millis, 1.second, greeter, greet)

或者

// 创建一个重复执行函数的定时任务,初始延迟为10毫秒,间隔时间为250毫秒
scheduler.scheduleWithFixedDelay(10.millis, 250.millis)(() => greeter ! greet)

固定频率

固定频率指的是两次连续的任务执行之间的时间差总是等于给定的间隔时间。下一次任务执行的时间是根据上一次任务执行的时间和间隔时间来计算的。如果当前任务是一个长时间运行的任务,那么下一次任务执行可能会立即开始,或者被跳过。因此,两次连续的任务执行之间的时间差可能是恒定的,也可能是零。

从Akka 2.6版本开始,Akka建议我们使用scheduleAtFixedRate()方法来创建固定频率的定时任务。所以,我们可以重写前面的例子,使用scheduleAtFixedRate()方法:

// 创建一个重复向actor发送消息的定时任务,初始延迟为100毫秒,间隔时间为1秒
scheduler.scheduleAtFixedRate(100.millis, 1.second, greeter, greet)

或者

// 创建一个重复执行函数的定时任务,初始延迟为10毫秒,间隔时间为250毫秒
scheduler.scheduleAtFixedRate(10.millis, 250.millis)(() => greeter ! greet)

Duration类

在上一篇利用akka模拟Spark的Master与Worker通信中,为什么那篇文章里的参数里面是Duration(3,TimeUnit.SECONDS),而不是这篇提供的这种。

Duration是一个表示时间长度的类,它有多种构造方法,可以接受不同类型的参数。这篇提供的那种参数是使用了一个隐式转换,把一个数字和一个时间单位组合成一个Duration对象。例如:

// 使用隐式转换,把3秒转换为一个Duration对象
3.seconds
// 等价于
Duration(3, TimeUnit.SECONDS)

这种隐式转换是由scala.concurrent.duration包提供的,它可以让我们用更简洁和直观的方式来表示时间长度。我们需要导入这个包才能使用这种隐式转换:

// 导入scala.concurrent.duration包
import scala.concurrent.duration._

所以参数里面是Duration(3,TimeUnit.SECONDS)或者3.seconds都是可以的。

参数里面是Duration(3,TimeUnit.SECONDS)可能是因为没有导入这个包,或者是为了避免歧义,或者是为了保持一致性。它们的效果是一样的,只是写法不同而已。

总结

在这篇文章中,介绍了Akka定时任务schedule()方法,它是一种在Akka actor系统中管理周期性执行任务的方式。看到了如何使用schedule()方法来创建单次执行和重复执行的定时任务,以及它们的不同类型的延迟。还了解了Akka定时任务schedule()方法的实现原理和精度问题。

有关Akka定时任务schedule()方法的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  5. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  6. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  7. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  8. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  9. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  10. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

随机推荐