Develope/Programming

[JAVA] 함수 주기적으로 돌리기 (스케쥴링)

고로이 2017. 10. 31. 11:34
반응형

요약

스케쥴링 기법 : scheduleAtFixedRate, 쿼츠, Spring Task, Spring aop



내가 주로 만드는 프로세스는 수집 프로세스인데


통계성 데이터의 경우 하루에 한번, 혹은 일주일에 한번, 한시간에 한번 등 작업을 한다



주로 하루에 한번한다.


쉘 스크립트를 만들어서 crontab에 올려도되지만,


그렇게 되면 서버마다 크론탭에 등록해야 하고, 권한문제 등 충돌이 일어날 수 있어서


웬만하면 자바 프로세스 상에서 스케쥴링 하는 것이 좋다.



이 때 이용하는 것이 Timer 클래스의 Schedule 관련 함수이다



void schedule(TimerTask task, Date time)

지정한 시간(time)에 지정한 작업(task)을 수행한다. 


void schedule(TimerTask task, Date firstTime, long period)

지정한 시간(firstTime) 부터 일정 간격(period)으로 지정한 작업(task)을 수행한다. 


void schedule(TimerTask task, long delay)

일정 시간(delay)이 지난 후에 지정한 작업(task)을 수행한다. 


void schedule(TimerTask task, long delay, long period)

일정 시간(delay)이 지난 후에 일정 간격(period)으로 지정한 작업(task)을 수행한다.


출처: http://javacan.tistory.com/entry/29 [자바캔(Java Can Do IT)]






이중 내가 쓰는 scheduleAtFixedRate 함수는 함수가 실행되면서 생긴 지연시간을 무시한다.

즉 특정 시간마다 실행시키려 할 때 유용하다.

scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
지정한 시간(firstTime)부터 일정 간격(period)으로 지정한 작업(task)을 수행한다. 

scheduleAtFixedRate(TimerTask task, long delay, long period)
일정한 시간(delay)이 지난후에 일정 간격(period)으로 지정한 작업(task)을 수행한다.



public void run(){
logger.info("");
CollectorWorker collectorWorker = new CollectorWorker();
collectorWorker.setConfPath(confPath);

Timer jobScheduler = new Timer();
jobScheduler.scheduleAtFixedRate(collectorWorker, calcTaskTime(startTime), period* 1000);

}



Timer jobScheduler = new Timer();
jobScheduler.scheduleAtFixedRate(collectorWorker, calcTaskTime(startTime), period* 1000);


이부분이다. 특정시간에 CollectorWorker의 run 함수를 매번 실행시킨다는 의미


함수의 내용은 


CollectorWorker.run 함수를, 


CalcTaskTime 만큼 대기해서 그 시작시간에 실행하고, 


period 시간마다 실행한다.




(20180430 수정)

public long calcTaskTime(int startTime) {

if(startTime > 23 || startTime < 0){
return 0;
}
Calendar calendar = new GregorianCalendar(Locale.KOREA);
calendar.set(Calendar.HOUR_OF_DAY, startTime);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);

long nowDate = new Date().getTime();

if (nowDate > calendar.getTime().getTime()) {
calendar.add(Calendar.DAY_OF_YEAR, 1);
}
long waiting = (calendar.getTime().getTime() - nowDate)/1000;
logger.info("Schedule Start Time : " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime()));
logger.info("Waiting : " + waiting+" sec");

return (int)waiting;
}



특정시간 (int) 를 집어넣으면 오늘날짜의 Date 타입을 반환해주는 함수


스케쥴 시작 시간과 웨이팅 시간을 출력해준다.




어차피 한번쓰이는 함수, period 시간이 중요하다




시작시간은 15시, 주기는 10초로 지정했다.




[ INFO] 17-10-31 14:51:05 : [main]manager.CollectorManager : Schedule Start Time : 2017-10-31 15:00:00

[ INFO] 17-10-31 14:51:05 : [main]manager.CollectorManager : Waiting : 535 sec


[ INFO] 17-10-31 15:00:00 : [Timer-0]worker.CollectorWorker : COLLECTOR START [2017-10-31 ~ 2017-11-01]

[ INFO] 17-10-31 15:00:00 : [Timer-0]worker.CollectorWorker : tb_facility_stat_daily START 

[ INFO] 17-10-31 15:00:02 : [Timer-0]worker.CollectorWorker : tb_facility_stat_monthly START 

[ INFO] 17-10-31 15:00:02 : [Timer-0]worker.CollectorWorker : tb_facility_stat_yearly START

 

[ INFO] 17-10-31 15:00:10 : [Timer-0]worker.CollectorWorker : COLLECTOR START [2017-10-31 ~ 2017-11-01]

[ INFO] 17-10-31 15:00:10 : [Timer-0]worker.CollectorWorker : tb_facility_stat_daily START

[ INFO] 17-10-31 15:00:10 : [Timer-0]worker.CollectorWorker : tb_facility_stat_monthly START 

[ INFO] 17-10-31 15:00:10 : [Timer-0]worker.CollectorWorker : tb_facility_stat_yearly START 




위와같이 15시에 시작하고

시작시간 + 10초뒤 에 다시 함수가 실행된다.






이따가 더써야징





2. Apach Quartz?



스프링 부트에서 쓸기회가 잇어서 한번 써봤다.

cronJob을 클래스 별로 돌리고, 등록하는 느낌이라 편하다는 느낌이 들엇다.

job의 추가에 용이할거같은 느낌


2-1. depenency 추가

<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
</dependency>


2-2. 스케쥴링


public class QuartzScheduler {
private SchedulerFactory schedulerFactory;
private Scheduler scheduler;

@Autowired
private QuartzMailJob mailQuartz;

@Autowired
private WorkflowMapper workflowMapper;

/**
* @throws SchedulerException
*/
@PostConstruct
public void start() throws SchedulerException {

schedulerFactory = new StdSchedulerFactory();
scheduler = schedulerFactory.getScheduler();

JobDetail job = JobBuilder.newJob(QuartzJob.class).withIdentity("SharedDataDeleteJob").build();
Trigger trigger = TriggerBuilder.newTrigger().
withSchedule(CronScheduleBuilder.cronSchedule("*/30 * * * * ?")).build();
}
}


보면 알겟지만 QuartzJob의 함수를 트리거에 등록시키는 형식



2-3. job 상세


import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class QuartzJob implements Job {
private Logger logger = LoggerFactory.getLogger(QuartzJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
checkDate();
}

public void checkDate() {


//TODO somthing


}
}


Job 을 implement 하면 execute 함수를 구현하라고 한다.


실행할 함수를 작성하면 된다.


*주의 : Spring Boot 내부에서는 자동으로 띄우는데, 이것이 부트와 쿼츠가 다른 메모리? 여튼 따로 뜬다. 그래서 DI를 주입할 수 없다. Dao 관련 Di를 다시 읽어와야하는 번거로움이 발생한다.






3. Spring Task


스프링을 쓰는 사람은 이것을 쓰면 편하다


주의할 점은 이것을 사용 할 때, ADHOC 기능을 사용하기 어렵다는 것이다.


스프링이 올라오는 동시에 application.xml 에 정의된스케쥴링도 실행되서 강제종료가 어렵다.




임의적으로 태스크만 실행을 막는법을 찾아봣지만 복잡해보여서 쿼츠로 변경한 기억이 난다.

지금 생각이 든건데 xml 파일을 분리하면 되지 않을까 한다.


<task:scheduled-tasks>
<task:scheduled ref="SyncWorker" method="runWorker" cron="${schedule.cron}"/>
</task:scheduled-tasks>


schedule.cron=0/20 * * * * *



SyncWorker의 runWorker 함수를 cron 처럼 돌린다는 것.



<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">





4. Spring Aop

Spring Boot 환경이었다.

AOP를 통해 스케쥴링을 만들 때 이런식으로 가능하다

@Scheduled(cron = "*/30 * * * * *")
public void schedule1(){

//todo


}


반응형