前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分布式系统中的定时任务全解(一)

分布式系统中的定时任务全解(一)

作者头像
九州暮云
发布2019-08-21 14:24:12
2.1K0
发布2019-08-21 14:24:12
举报
文章被收录于专栏:九州牧云九州牧云

概述

在网站系统里面定时任务是一个重要和不可缺的角色,很多地方需要使用定时执行一项任务。比如,订单系统的接单超时、支付超时,结算系统的定时结算、奖励计算,第三方的认证信息刷新(微信的token),dsp等推广平台数据定时对接,缓存数据的定时更新等。

定时任务实现方式

Timmer定时器

这时最简单和最基础的了,学习过计算机课程的都能知道也都能写。

虽然常见,这里也简单的说几个点:

触发时间点

Timer有两种指定执行时间的方式,一种是给一个时间间隔(Interval),另一种就是给定一个具体的执行时间。

输入图片说明
输入图片说明

这时,timer的所有方法,其中画红圈圈的。第一个是在指定的时间点触发,给的参数是一个Date类型;第二个是按照指定的时间间隔触发,给的参数是一个long类型。这两种在实际工程中都是会被用到的。

Timer和TimerTask之间的关系

先看一个最常见的Timer使用示例:

代码语言:javascript
复制
public class MyTimeTask {
   public static void main(String[] args){
      Timer timer = new Timer(); 
      timer.schedule(new MyTask1(), 60 * 1000);
      timer.schedule(new MyTask2(), 120 * 1000);
    }

    class MyTask1 extends TimerTask {
       public void run(){
        System.out.println("running1....");
      }
    }

    class MyTask2 extends TimerTask {
       public void run(){
        System.out.println("running2....");
      }
    }
}

可以看到,一个Timer是可以调度多个Task的,这个也可以从Timer的源代码看出来:

输入图片说明
输入图片说明

mainloop循环在不断的读取queue中的task,逐个执行。对于按间隔期不断执行的task,会被计算下次执行时间后被重新放入到队列中。

这里的queue是一个优先级队列,会按照task的执行时间排序,最近的task会被先取出来执行。

输入图片说明
输入图片说明

这里想说的是什么呢,在工程中最好不要一个task就创建一个timer,这样有点过于浪费系统资源,因为一个timer就是一个线程,而且在不断的消耗系统的cpu资源。

quartz

quartz是java里面最流行的定时任务调度框架,python里面的定时任务框架APScheduler也是基于Quartz,实现了Quartz的所有功能。

这里把quartz的内容多介绍一些,因为后续集群部分涉及到的elastic-job,同样是基于quartz实现的。要了解更多关于quartz的只是,推荐两个网站:

官网教程目录页

http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/

以梦为马的个人博客(一个系列的3篇)

http://blog.csdn.net/ahmuamu/article/details/50364769

quartz的只要构成是schedule、trigger、job三元素的体系,体系中还提供了三者各自的Listener用来监听他们的事件和对事件作出响应。以下是一个最原始的、最基础的quartz使用示例代码(来自官网):

代码语言:javascript
复制
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

  Scheduler sched = schedFact.getScheduler();

  sched.start();

  // define the job and tie it to our HelloJob class
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1")
      .build();

  // Trigger the job to run now, and then every 40 seconds
  Trigger trigger = newTrigger()
      .withIdentity("myTrigger", "group1")
      .startNow()
      .withSchedule(simpleSchedule()
          .withIntervalInSeconds(40)
          .repeatForever())
      .build();

  // Tell quartz to schedule the job using our trigger
  sched.scheduleJob(job, trigger);

贴在这里,是为了让大家理解三者之间的关系。job封装任务执行的代码,trigger封装任务执行的时间信息,schedule绑定job和trigger执行任务调度。

在java世界里,spring已经是无所不在,接下来简单的看一下spring集成quartz需要做的事情(理解的上述的代码,也就容易理解,为什么spring配置文件里要配置这些东西了)。

1.首先就是要把quartz的包引入进来,添加maven引用:

代码语言:javascript
复制
<dependency>
    <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
</dependency>

2.定义job,定义job有两种方式,一种是使用MethodInvokingJobDetailFactoryBean封装自己的bean,这样自己的bean可以是一个任意的pojo;第二种是使用JobDetailFactoryBean,同时让自己的job扩展QuartzJobBean,实现executeInternal的抽象方法。

具体可以参考(这里给的比较详细):http://websystique.com/spring/spring-4-quartz-scheduler-integration-example/

3.定义trigger 这里可以定义简单的trigger(SimpleTriggerFactoryBean),他使用的是jdk的timer做为调度;也可以定义一个cron表达式类型的trigger(CronTriggerFactoryBean)去定义一个更语义化的触发表达式。

关于cron表达式的知识,可以参见这里:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-06.html

4.定义scheduler,绑定trigger和job

spring-scheduled注解

spring自己也提供了一个轻量级的定时任务工具,而且是在core包里面。可以使用scheduled注解,也可以仅使用xml配置。虽然说它轻量级,但是他实现了quartz支持的两种时间触发机制,简单的和cron表达式的。说他轻量级,也是因为它不能支持quartz能够支持的集群功能

这里先推荐一个spring的官方版本(如果对spring-scheduled没有概念,需要先查baidu了解一些之后再看这里,因为这个写的比较简洁):http://spring.io/guides/gs/scheduling-tasks/

接下来一块看下scheduled的注解使用,也是两种用法,一个是普通的timer类似调度,一种是cron表达式方式调度。

这里只看cron表达是方式的,普通方式的请见上述网址。以下示例来自于http://howtodoinjava.com/。关于spring-scheduled的使用推荐以下几个网址:

http://howtodoinjava.com/spring/spring-core/4-ways-to-schedule-tasks-in-spring-3-scheduled-example/

http://www.cnblogs.com/Gyoung/p/5339242.html

http://spring.io/guides/gs/scheduling-tasks/

1.引入task命名空间,开启注解自动识别

代码语言:javascript
复制
< ?xml  version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:task="http://www.springframework.org/schema/task"
 xmlns:util="http://www.springframework.org/schema/util"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util/ http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <task:annotation-driven />

</beans>

2.在需要调度的方法上添加@scheduled注解

代码语言:javascript
复制
@Scheduled(fixedDelay =30000)
public void demoServiceMethod () {... }

@Scheduled(fixedRate=30000)
public void demoServiceMethod () {... }

@Scheduled(cron="0 0 * * * *")
public void demoServiceMethod () {... }

好了看今天先说到这里,下一篇整理一下quartz基于数据库的集群实现以及elastic-job的使用。最后在给出一篇elastic-job的解析,以及从解析看实际使用场景的实现。

##其它参考资料

  • 码农的士:https://www.dexcoder.com/blog/tag/1582,这系列文章有四篇,博主主要写了使用spring 3 和 Quartz2 实现定时任务的方式,并给了实现添加、修改、删除定时任务等操作的思路,最后探讨了集群、分布式环境下定时任务的实现思路
  • 美团点评技术团队:Quartz应用与集群原理分析
  • 分布式定时任务:http://www.jianshu.com/nb/6374890,这系列文章有三篇,主要是作者使用spring4.x+quartz2.2.x 1.1 实现分布式定时任务的记录,文中分析了quartz2.2.x实现定时任务的原理
  • 分布式任务调度平台XXL-JOB:https://github.com/xuxueli/xxl-job,XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。可以学习作者的实现。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定时任务实现方式
    • Timmer定时器
      • quartz
        • spring-scheduled注解
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档