기본 콘텐츠로 건너뛰기

[ SOLR] Quartz Schedule 을 이용한 DataImport 작업 수행

  구글 코드 및 GitHub  Solr Data Import 관련된 오픈 소스들이 많다 물론 Quartz를 이용한 스케줄러에 대한 것도 많이 존재한다 지금 정리하는 내용은 GitHub에 존재하는 사이트를 기준으로 했고이 사이트는 Google Code 의 소스를 기반으로 해서 확장(?)한 것으로 보인다사용한 코드도 소스를 적용할 환경과 요구에 맞도록 변경해서 사용한 것이기 때문에 실제 사이트에서 제시하고 있는 내용과는 다를 수 있다물론 설정 방법은 동일하게 사용한다.

Scheduler 관련 사이트들

  스케줄러 기능을 사용하기 위해서는 다음과 같은 구성요소가 필요하다.

Scheduler 설치와 구성

  설치와 구성은 다음과 같이 설정하면 된다.

  DataImportScheduler-0.0.1.jar 파일을 Solr 설치 서버 (주로 톰캣)의 클래스패스 (별다른 설정이 없다면 WEB-INF/lib) 에 복사한다구동에 필요한 배포 대상 Jar 들은 아래와 같다.
  • Quartz-2.2.1.jar
  • Quartz-jobs-2.2.1.jar
  • Fluent-hc-4.3.2.jar
  • jta-1.1.jar
당연한 것이지만, Solr, Lucene, Http, slf4j 등의 관련 Jar 들은 이미 구성된 Solr 서버에 존재하므로  빠진 부분만 추가로 배포하면 된다.

  웹 설정 (별다른 설정이 없다면 WEB-INF/web.xml) 에 다음과 같이 리스너를 설정한다. Solr 서버의 ServletContextListener를 상속하여 Context 가 초기화 및 제거 될 때 Quartz Scheduler On/Off 처리하기 위한 연결고리로 사용한다.


...
<!-- Listener for dataimport-scheduler. -->
<listener>
    <listener-class>com.msfl.tools.solr.handler.dataimport.scheduler.ApplicationListener</listener-class>
</listener>
...

DataImportScheduler 는 기본적으로 SolrResourceLoader 를 사용하여 경로를 판단하게 된다. (물론 소스에서 이를 수정해서 다른 폴더로 사용할 수도 있다기본적으로는 Solr 가 구동될 때 옵션으로 지정했던 solr.solr.home 에 지정한 Solr Home 경로를 기준으로 한다. (ex. -Dsolr.solr.home=Solr홈경로이 폴더를 기준으로 "conf" 폴더를 찾게 되므로 (ex. Solr.solr.home=C:\Solr 라고 지정했으면 파일을 찾는 경로는 C:\Solr\conf 가 된다이 폴더에 quartz.properties 파일을 생성한다이 파일은 Quartz 구동과 관련된 설정 정보를 위한 것으로 전체 Core 에 대해서 동작하게 된다템플릿은 다운로드 한 jar 파일 내의 templates 폴더 밑의 파일을 참조하면 된다없다면 Quartz  기본 설정 정보 를 참고하도록 한다일반적으로 사용할 경우는 이 설정을 변경할 이유가 없다아래는 기본 사용할 정보를 설정한 파일의 내용이다.


#============================================================================
# Configure Main Scheduler Properties 
#============================================================================

org.quartz.scheduler.instanceName: SolrDataImportScheduler
org.quartz.scheduler.instanceId: AUTO
org.quartz.scheduler.skipUpdateCheck: true

#============================================================================
# Configure ThreadPool 
#============================================================================

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 3
org.quartz.threadPool.threadPriority: 5

#============================================================================
# Configure JobStore 
#============================================================================

org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

#============================================================================
# Configure Plugins
#============================================================================

#org.quartz.plugin.triggHistory.class: org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.jobInitializer.class: com.msfl.tools.solr.quartz.plugins.xml.SolrXMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileNames: quartz_schedule.xml
org.quartz.plugin.jobInitializer.failOnFileNotFound: true
org.quartz.plugin.jobInitializer.scanInterval: 120
org.quartz.plugin.jobInitializer.wrapInUserTransaction: false 

Solr home/conf 폴더에 (위와 동일 경로에 quartz_schedule.xml 파일을 생성한다. (위의 quartz.properties에 설정한 fileNames 값과 동일한 파일이 파일은 Quartz 에 의해서 수행되어야 할 Jobs 와 실행 Triggering 에 대한 정보를 위한 것으로 템플릿은 다운로드 한 jar 파일 내의 templates 폴더 밑의 파일을 참조하면 된다없다면 Jobs  대한 설정 정보 를 참고하도록 한다일반적으로 Full / Delta, 일회 / 주기적 호출 등의 정보를 상황에 맞도록 설정한다.

<?xml version="1.0" encoding="UTF-8"?>
<job-scheduling-data xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
                             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                              xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData
                                                         http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd "
                             version="1.8">
    <!-- Consult the xsd for more complete scheduling options. -->    
    <schedule>
        <job>
            <name>DataImportJob</name>
            <job-class>com.msfl.tools.solr.dataimport.quartz.DataImportJob</job-class>
            <job-data-map>
                <!-- Url for solr webapp. Default is http://localhost:8080/solr. -->
                <entry>
                    <key>solrUrl</key>
                    <value>http://localhost:8080/solr</value>
                </entry>
                <!-- A comma-separated list of cores. If not provided, uses default core. -->
                <entry>
                    <key>cores</key>
                    <value>core1,core2, ...</value>
                </entry>
            </job-data-map>
        </job>
        <trigger>
            <!-- This is a simple trigger. http://quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-05 -->
            <simple>
                <name>FullDataImportJobOnce</name>
                <job-name>DataImportJob</job-name>
                <job-data-map>
                    <!-- Note that settings can go in job's job-data-map or trigger's job-data-map depending on which is appropriate. -->
                    <!-- full-import or delta-import.   Default is full-import. -->
                    <entry>
                        <key>command</key>
                        <value>full-import</value>
                    </entry>
                    <!-- Other settings are clean, optimize, and commit. -->
                </job-data-map>
                <!-- Execute 15 seconds after solr starts. -->
                <start-time-seconds-in-future>15</start-time-seconds-in-future>
                <!-- Only perform once. -->
                <repeat-count>0</repeat-count>
                <!-- Since not repeating, repeat-interval set to 0. -->
                <repeat-interval>0</repeat-interval>
            </simple>
        </trigger>
        <trigger>
            <!-- This is a cron trigger. See http://quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-06 -->
            <cron>
                <name>DailyDeltaDataImport</name>
                <job-name>DataImportJob</job-name>
                <job-data-map>
                    <entry>
                        <key>command</key>
                        <value>delta-import</value>
                    </entry>
                </job-data-map>
                <!-- Every night at 4:30. -->
                <!-- seconds minutes hours day-of-month month day-of-week -->
                <cron-expression>0 30 4 * * ?</cron-expression>
            </cron>
        </trigger>   
    </schedule>   
</job-scheduling-data>

Cron 표현식

  Java에서 스케줄을 처리할 때 많이 사용하는 것이 Quartz 이고 여기서 제공하는 Trigger SimpleTrigger  CronTrigger 가 있다. 
  • SimpleTrigger - 단순히 Interval, Delay, Repeat Times 등의 설정을 통해서 구동된다.
  • CronTrigger - Linux  cron 표현식을 사용한 설정을 통해서 구동된다.

필드 설명

  cron표현식은 ' ' 으로 구분되는 6 ~ 7 개의 문자 (숫자영숫자기호를 사용하여 지정한다 아래의 표는 사용할 수 있는 필드를 정리한 것이다.


필드
필수
허용 
특수 설정 문자
 (seconds)
YES
0 ~ 59
, - * /
 (minutes)
YES
0 ~ 59
, - * /
 (hours)
YES
0 ~ 23
, - * /
날짜 (day of month)
YES
1 ~ 31
, - * / ? L W
 (month)
YES
1 ~ 12 또는 JAN ~ DEC
, - * /
요일 (day of week)
YES
1 ~ 7 또는 SUN ~ SAT
, - * / ? L #
 (year)
NO
'' 또는 1979 ~ 2099
, - * /

특수 설정 문자 
  • , : 여러 값을 의미하며시간에 14, 18 로 지정했다면 24시간 기준으로 오후 2시와 오후 6시가 된다.
  • *  :모든 값을 의미하며초에 사용하면 매초분에 사용하면 매분이 된다.
  • ? : 값을 정하지 않는 것으로 일과 요일에 사용 가능하며일에 사용하면 어떤 요일도 상관없다는 뜻이 된다.
  • -  :범위를 의미하는 것으로 0 - 10 이면 0 부터 10까지를 의미한다 마지막 값인 10도 포함된다는 점을 주의해야 한다.
  • / : 증분을 의미하는 것으로 분에 0 / 5 로 지정하면 0분 부터 매 5분마다를 의미한다, 0, 5, 10, 15, 20, … 을 의미한다.
  • L : 마지막 값을 의미하는 것으로 날짜에 사용하면 해당 월의 마지막 일자를 의미한다. 30 또는 31 이며 2월은 28 (윤달은 29)이 된다.
  • W : 주중 평일 (Weekday, MON, TUE, WED, THU, FRI) 을 의미하는 것으로 날짜와 같이 쓰면 그 날짜가 주중인 날을 의미한다.
  • # : n 번째를 의미하는 것으로 예를 들어 특정 달의 몇 번째 요일을 지정하는 경우에 사용한다, 3번째 월요일은 2#3 이다. 2  SUN 부터 시작하므로 MON을 의미하고, 3 은 3번째를 의미한다. MON#3 으로 표현해도 된다.
사용 예


표현식
의미
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분까지  분마다 동작
0 0/5 14 * * ?
매일 오후 2시부터 2 55분까지  5분마다 동작
0 0/5 14,18 * * ?
매일 오후 2 부터 2 55분까지  5분마다오후 6 부터 6 55분까지  5분마다 동작
0 0-5 14 * * ?
매일 오후 2시부터 5분까지 매분  동작
0 10,44 14 ? 3 WED
매년 3월의 수요일마다 오후 2 10분과 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
매달 3번째 금요일 오전 10 15  동작
0 0 12 1/5 * ?
매달 첫날부터 5일마다 12(정오 동작
0 11 11 11 11 ?
매년 11 11 오전 11 11 0  동작

주의할 점 
  • 날짜와 요일에 ? 를 지정하면 서로 배타적으로 동작한다날짜에 ? 를 사용하면 요일에 사용할 수 없고요일에 사용하면 날짜에 사용할 수 없다는 것이다매달 x일이 y요일인 경우가 발생할 수 없기 때문이다.
  • 범위(-)를 사용할 때는 항상 마지막 값이 포함된다특정 시간을 제외할 경우는 범위를 분리해서 사용해야 한다예를 들어 6시를 제외한 모든 시간이라면 0-5,7-23 으로 표현해야 한다. 0-6으로 하면 6이 포함된다.
  • 날짜에 L  W를 함께 사용하면 매월 마지막 평일의 의미가 된다.
  • 요일에 x#5를 사용하면 5번째 주의 x요일을 의미한다.
  • 해외에서는 썸머타임 (daylight saving) 을 고려해야 한다자정부터 새벽 1시까지의 시간은 썸머타임 시행 시에는 건너 뛰거나 반복해서 실행될 가능성이 있다.
결론

  위와 같이 설정을 한 후에 Solr 를 구동하면 Log 상에 Quartz 와 관련된 Scheduler 가 동작하는 내용을 확인할 수 있다그러고 quartz_shedule.xml  job  trigger 가 제대로 설정된 상태라면 해당 Trigger 조건에 맞게 DataImport 가 호출되는 것을 확인할 수 있다만일 제대로 설정한 상태지만 Trigger 가 구동되지 않는다면 주로 cron trigger 일 경우에는 cron-expression 이 제대로 설정되지 않았을 경우가 많다. Cron-expression 은 Cron Marker 사이트 를 통해서 생성할 수 있다.

댓글