zoukankan      html  css  js  c++  java
  • 作业调度哪种方式好,最终选了Quartz

          随着云平台,大数据等的出现,用户或潜在访问者越来越想免费体验产品,特别是想申请系统(比如开虚拟机,启动docker等),可是系统硬件资源有限,那怎么才能让用户实际操作云平台资源呢,那就是免费三天或七天的使用期(也有是一天的使用期),若过期了不花钱续费,系统就要自动清理云环境申请的资源(好多云厂商都是如此,比如某云,免费试用到期了,会发邮件t提醒续费),这时就用到了作业调度的功能,这就要用定时器(它可以当做定时的任务,到期会执行,就像闹钟),java生态中有原生的定时器库,也有第三方的比如Quartz,

    下面我就用例子说明下如何使用

    1.  java的原生定时器组件Timer和TimerTask

    Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,它可以计划执行一个任务一次或反复多次。

    Timer类中常见方法

    public class Timer {
        /**
        * task queue
        */
        private final TaskQueue queue = new TaskQueue();
        /**
         * The timer thread.
         */
        private final TimerThread thread = new TimerThread(queue);
    
        public Timer( );
        public Timer(boolean isDaemon);
        public Timer(String name);
        public Timer(String name, boolean isDaemon);
    
        //在指定延迟后执行指定的任务
        public void schedule(TimerTask task, long delay);
        //在指定的时间执行指定的任务
        public void schedule(TimerTask task, Date time);
        //指定的任务从指定的延迟后开始进行重复的固定延迟执行
        public void schedule(TimerTask task, long delay, long period);
        //指定的任务在指定的时间开始进行重复的固定延迟执行
        public void schedule(TimerTask task, Date firstTime, long period);
    
        //指定的任务在指定的延迟后开始进行重复的固定速率执行
        public void scheduleAtFixedRate(TimerTask task, long delay, long period);
        //指定的任务在指定的时间开始进行重复的固定速率执行
        public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period);
    
        //终止计时器,丢弃所有当前已安排的任务
        public void cancel( );
        //从计时器的任务队列中移除所有已取消的任务
        public int purge( );
    
    }

    而TimerTask是一个抽象类,实现了Runnable接口,所以也就具备多线程的能力,它的子类代表一个可以被Timer计划的任务。

    下面是一个简单的例子,编写任务代码

    /**
     * 
     * @author dgm
     * @describe "测试打印定时器"
     * @date 2017年4月10日
     */
    public class PrintTimerTask extends TimerTask {
    
        private String name;
        
        public PrintTimerTask(String name) {
            super();
            this.name = name;
        }
    
        @Override
        public void run() {
           if (System.currentTimeMillis( ) - scheduledExecutionTime( ) > 5000) {
                return;
            }
    
            System.out.println("线程:"+ name +"***** 在 执行。。"); 
        }
    }

     编写测试代码

    /**
     * 
     * @author dgm
     * @describe "测试定时器"
     * @date 2017年4月10日
     */
    public class TimeTaskTest {
    
        public static void main(String[] args) {
            Timer timer = new Timer();
            //设置3秒后启动任务
            timer.schedule(new PrintTimerTask("name-0"), 3000);
            PrintTimerTask secondTask = new PrintTimerTask("name-1");
            // 1秒后启动任务,以后每隔3秒执行一次线程
            timer.schedule(secondTask, 1000, 3000);
            Date date = new Date();
            // 以date为参数,指定某个时间点执行线程
            timer.schedule(new PrintTimerTask("name-3"), new Date(
                    date.getTime() + 5000));
        }
    }

     测试结果如图:

    注意此方法schedule和scheduleAtFixedRate的区别:

    虽然jdk原生能实现需要的定时清理功能,查阅了资料后发现还有更好的方法,接着往下看 

    2.  ScheduledExecutorService(一个接口)

    ScheduledExecutorService,是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

    一个小例子如下

    编写要执行的线程任务

    public class PrintScheduledExecutor implements Runnable {
    
        private String jobName;
    
        public PrintScheduledExecutor(String jobName) {
            this.jobName = jobName;
        }
    
        @Override
        public void run() {
    
            System.out.println(jobName + " 正在运行中!!!");
        }
    }

    测试定时任务执行

    public static void main(String[] args) {
           
           ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
    
            long initialDelay = 1;
            long period = 1;
            service.scheduleAtFixedRate(new PrintScheduledExecutor("job1"),
                    initialDelay, period, TimeUnit.SECONDS);
    
            service.scheduleWithFixedDelay(new PrintScheduledExecutor("job2"),
                    initialDelay, period, TimeUnit.SECONDS);
         }

     注意scheduleAtFixedRate和scheduleWithFixedDelay:

     这个scheduledexecutorservice似乎是不错,我又想有没有更好的调度框架,这时又找到了Quartz(也是我现实中用到的定时调度库)

    3.  Quartz要登场了,强大无比

           简单介绍下,Quarzt是一个项目中定时执行任务的开源项目,Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。它的结构图如图示:

    job任务类:表示的任务是什么(比如自定义的任务种类比如清理试用账号申请的资源),需要定时执行代码的类。

    JobDetail:配置任务类的细节,即注入任务类和指定任务类的方法,是一个可执行的工作,它本身可能是有状态的。

    trigger:即触发器,代表一个调度参数的配置,配置调用的时间,表示何时触发job任务。

    调度工厂(scheduler):是一个计划调度器容器,容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。

      下面以案例说下如何使用(由于我不是单独使用的,是和spring整合使用)

    创建自定义job类(伪代码)

    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.context.ApplicationContext;
    
    /**
     * 销毁试用账号老师的docker集群
     * Created by dongguangming on 2017/4/14.
     */
    public class DeTryTeacherAppJob implements Job 
    {
        public static Logger logger = Logger.getLogger(DeTryTeacherAppJob.class);
        
        @Override
        public void execute(JobExecutionContext jobCtx) throws JobExecutionException 
        {
        logger.info("[" + currentTime + "]判定失效试用账号任务开始");
            Map datamap = jobCtx.getJobDetail().getJobDataMap();
            ApplicationContext ctx = (ApplicationContext) datamap.get("applicationContext");
            UserService userService = (UserService) ctx.getBean("userService");
            AppService appService = (AppService) ctx.getBean("appService");
           // 执行业务操作
            ...............
        }
    }

    然后再spring配置任务类的bean和配置触发器(时间)

     <!--销毁试用账号的docker集群job -->
        <bean  name="deTryTeacherAppJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"
            p:jobClass="com.cstor.docker.job.DeTryTeacherAppJob"
            p:applicationContextJobDataKey="applicationContext">
        </bean>
    <!-- 参考http://www.cnblogs.com/ypf1989/p/5552426.html http://www.cnblogs.com/30go/p/5761917.html -->
        <bean id="deTryTeacherAppJob" class="org.springframework.scheduling.quartz.CronTriggerBean"
              p:jobDetail-ref="deTryTeacherAppJobDetail"
              p:cronExpression="0 0/3 * * * ?"
            />

    最后配置调度工厂并且注入配置好的触发器

     <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
                <property name="triggers">
                    <list>
                        <ref bean="deTryTeacherAppJob">
                    </list>
                </property>
        </bean>

     现在可以启动测验了,测试时可以调整触发时间(比如每分钟),下面是con表达式样板

    "0 0 12 * * ?" 每天中午12点触发
    "0 15 10 ? * *" 每天上午10:15触发
    "0 15 10 * * ?" 每天上午10:15触发
    "0 15 10 * * ? *" 每天上午10:15触发
    "0 15 10 * * ? 2005" 2005年的每天上午10:15触发
    "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
    "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
    "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
    "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
    "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
    "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
    "0 15 10 15 * ?" 每月15日上午10:15触发
    "0 15 10 L * ?" 每月最后一日的上午10:15触发
    "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发 
    "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
    "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

    小结:

    1.  Timer在执行定时任务时只创建一个线程。

    2.  Timer线程并不捕获异常,TimerTask抛出的未检查的异常会终止timer线程,导致剩下的任务没法执行(这很不好)。

    3.  ScheduledExecutorService继承于ExecutorService,ScheduledThreadPool内部是线程池,支持多个任务并发执行.

    4.  Quartz很灵活,和spring集成的很好。

    d...........

    Timer&TimerTask→→→→→ScheduledExecutorService(体系复杂)→→→→→Quartz


    参考:

    0.  Java Timer vs ExecutorService? 

    1  https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html

    2.  几种任务调度的 Java 实现方法与比较: https://www.ibm.com/developerworks/cn/java/j-lo-taskschedule/

    3.  cron表达式生成工具: http://cron.qqe2.com/

    4.  http://www.quartz-scheduler.org/documentation/

  • 相关阅读:
    Request的方法演示
    Request内置对象
    Servlet运行机制
    Servlet生命周期
    Servlet概念与配置
    http协议
    数据库连接池
    代码管理git托管到码云(github)开源中国
    事务
    类的执行先后顺序
  • 原文地址:https://www.cnblogs.com/dongguangming/p/12677153.html
Copyright © 2011-2022 走看看