หน้าเว็บ

วันเสาร์ที่ 1 กันยายน พ.ศ. 2555

ตั้งตารางเวลาทำงานด้วย java cron job : Quartz Scheduler

        cron job คืออะไร
cron job ก็คือ  การตั้งเวลาให้โปรแกรมทำงานบางอย่าง  ตามเวลาที่เราได้กำหนดไว้  เช่นบอกว่า  ทุก 09.30 น. ของวันเสาร์  ให้ทำการอัพเดทฐานข้อมูล  หรือ ทุก 12.00 น. ของวัน จันทร์ พุธ ศุกร์  ให้ส่งข้อมูลไปยังปลายทางที่กำหนด  หรือ  ทุกวันที่ 1 ของทุกเดือน  ให้เคลียร์  log ข้อมูล ของเดือนก่อนหน้าทั้งหมด  เป็นต้น  ความหมายประมาณนี้ครับ

        อันนี้เป็นตัวอย่างที่ผมได้ใช้ในโปรเจ็คครับ  เป็นโปรแกรมส่งข้อมูลด้วยเทคโนโลยี JMS (Java Message Service) คือ User จะเป็นคนตั้งเวลาเอง  ว่าจะให้ส่งข้อมูลไปยังปลายทางตอนไหน  วันไหนบ้าง  ผมก็ใช้ Quartz เนี่ยแหล่ะครับทำ



ต่อมาเรามาดูวิธีเขียนกันดีกว่า  ในที่นี้จะใช้ java quartz scheduler ซึ่งเป็น open source java ตัวนึงครับ  เรียนรู้  และเข้าใจง่ายมากครับ  สามารถเรียนรู้ได้ที่นี่เลย http://quartz-scheduler.org/

มาเริ่มงานจริงกันดีกว่า  ผมใช้ maven จึงต้องมี dependencies ดังต่อไปนี้
อันนี้เป็น version ล่าสุดที่ผมใช้น่ะครับ
        
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.1.6</version>
</dependency>
        
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>3.1.2.RELEASE</version>
    <type>jar</type>
</dependency>
        
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.6.4</version>
</dependency>
        
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.0.6</version>
</dependency>
ต่อมา simple java code
กำหนดงาน หรือ Class ที่จะให้ทำตาม schedule ดังนี้

package com.blogspot.na5cent;

/**
 *
 * @author redcrow
 */
public class Worker {

    public void run() {
        System.out.println("Hello world!");
    }
}

package com.blogspot.na5cent;

import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;

/**
 *
 * @author redcrow
 */
public class Test {

    public static void main(String[] args) {
        try {

            //get schedule 
            SchedulerFactory sf = new StdSchedulerFactory();
            Scheduler sched = sf.getScheduler();

            //สร้าง object หรือ งานที่จะให้ ทำตาม scheduler
            Worker worker = new Worker();

            //สร้าง job detail เพื่อบอกรายละเอียดงานที่จะให้ทำ พร้อมระบุ id ของ job นั้น  โดยจะต้องมี id ไม่ซ้ำกันด้วย
            JobDetail job = createJob(worker, "workJob1");

            //สร้าง trigger คือตัวตั้งเวลาการทำงาน  ว่าจะให้ทำตอนไหน พร้อมระบุ id ของ trigger นั้น
            Trigger trigger = createTrigger("workTrigger1");

            //นำ job และ trigger ยัดลงใน schedule
            sched.scheduleJob(job, trigger);

            //start งานนั้น  ให้เริ่มทำงาน
            sched.start();
                    
        } catch (ParseException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SchedulerException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


    //method สร้าง job detail
    private static JobDetail createJob(Worker worker, String identity) throws ClassNotFoundException, NoSuchMethodException {
        MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();

        //set object งานลงไป
        jobDetail.setTargetObject(worker);

        //บอกว่าจะให้ทำที่ method ไหน ของ object นั้น  ในที่นี้คือให้ทำที่ method run
        jobDetail.setTargetMethod("run");

        //ระบุคีย์  โดยจะต้องไม่ซ้ำกัน
        jobDetail.setName(identity);
        jobDetail.setConcurrent(false);
        jobDetail.afterPropertiesSet();
        return jobDetail.getObject();
    }

    //method สร้าง trigger 
    private static Trigger createTrigger(String identity) throws ParseException {
        //ตั้งเวลา  จากตัวอย่างคือ ทุกๆ 5 วินาที
        CronExpression cx = new CronExpression("0/5 * * * * ?");
        CronScheduleBuilder atHourAndMinuteOnGivenDaysOfWeek = CronScheduleBuilder.cronSchedule(cx);
        return TriggerBuilder.newTrigger()
                .withSchedule(atHourAndMinuteOnGivenDaysOfWeek) //set เวลา
                .withIdentity(identity) //ระบุคีย์ trigger โดยจะต้องไม่ซ้ำกัน
                .startNow() 
                .build();
    }
}


สังเกตตรง CronExpression(“0/5 * * * * ?”);   (ตัวอย่างนี้ไม่ได้ใส่หลักที่ 7 มา)
ที่จริงมันคือการแทนตัวแปร * ทั้งหมด 7 หลักครับ  แต่ละหลักมีความหมายดังนี้


* หลักที่ 1 คือ วินาที  บังคับต้องมี ค่าที่แทนลงไปได้ คือ จาก 0 – 59 วินาที (0/5 คือ ทุกๆ 5 วินาที)
* หลักที่ 2 คือ นาที บังคับต้องมี  ค่าที่แทนลงไปได้ คือ จาก 0 – 59 นาที
* หลักที่ 3 คือ ชั่วโมง  บังคับต้องมี  ค่าที่แทนลงไปได้ คือ จาก 0 – 23 นาฟิกา
* หลักที่ 4 คือ วันที่ของเดือน  บังคับต้องมี  ค่าที่แทนลงไปได้ คือ  ตั้งแต่วันที่ 1 - 31
* หลักที่ 5 คือ เดือน  บังคับต้องมี  ค่าที่แทนลงไปได้  คือ ตั้งแต่เดือนที่ 1 -12 หรือ JAN-DEC
* หลักที่ 6 คือ วันในสัปดาห์นั้น  บังคับต้องมี  ค่าที่แทนลงไปได้  คือ 1 – 7 หรือ SUN - SAT
* หลักที่ 7 คือ ปี  ไม่จำเป็นต้องมีก็ได้  ถ้าไม่มี  มันก็จะทำงาน ทุกๆ ปี นั่นเองครับ

ตัวอย่าง เช่น 0 30 13 10 1 ? 2013 คือ  ให้ทำงานเฉพาะ  วันที่ 10 มกราคม 2556(2013)  เวลา 13.30 น. เท่านั้น



ถ้าต้องการให้ทำงานตามวันเวลาที่กำหนด สามารถทำได้ดังนี้ คือ ให้แก้ตรง method createTrigger ใหม่ ตัวอย่างคือ ทำงานทุกๆ 09.30 ของ วัน จ. อ. พ. พฤ. ศ.
    
private static Trigger createTrigger(String identity) 
    throws ParseException {
    //CronExpression cx = new CronExpression("0/5 * * * * ?");
    Integer[] days = {2, 3, 4, 5, 6}; //1 คือ วันอาทิตย์ 7 คือวันเสาร์  จะใส่กี่ตัวก็ได้ 
    CronScheduleBuilder atHourAndMinuteOnGivenDaysOfWeek = CronScheduleBuilder.atHourAndMinuteOnGivenDaysOfWeek(9, 30, days);//cronSchedule(cx);
    return TriggerBuilder.newTrigger()
           .withSchedule(atHourAndMinuteOnGivenDaysOfWeek)
           .withIdentity(identity)
           .startNow()
           .build();
}

การลบ job  ทำได้ดังนี้
จากตัวอย่างคือ workJob1

...
...
    public static void main(String[] args) {
        try {
            SchedulerFactory sf = new StdSchedulerFactory();
            Scheduler sched = sf.getScheduler();

            JobKey jobkey = new JobKey("workJob1");
            sched.deleteJob(jobkey);  //ลบ job นั้นๆ  

        } catch (SchedulerException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
เรียนรู้เพิ่มเติมได้ที่ http://quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/crontrigger

1 ความคิดเห็น: