czwartek, 28 czerwca 2012

Apache Camel - klastrowanie Quartz.



 [Apache Camel 2.8.0, Quartz 1.8.4, Jboss 6]


Konfiguracja Quartz w Spring dla środowiska klastrowego.

Wymagane zależności Maven:

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-oracle</artifactId>
            <version>1.8.4</version>          
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-quartz</artifactId>
            <version>2.8.0</version>
        </dependency>   

W pliku Context.xml umieszczamy:

    <bean id="quartz" class="org.apache.camel.component.quartz.QuartzComponent">
        <property name="scheduler" ref="scheduler"/>
        <property name="autoStartScheduler" value="true"/>
    </bean>
   
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="overwriteExistingJobs" value="true"/>
        <property name="applicationContextSchedulerContextKey" value="applicationContext" />

        <property name="autoStartup" value="false"/>
        <property name="schedulerContextAsMap">
            <map>
                <entry key="CamelQuartzCamelContext" value-ref="camelContextCore"/>
            </map>
        </property>
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.scheduler.instanceName">${org.quartz.scheduler.instanceName}</prop>
                <prop key="org.quartz.scheduler.instanceId">${org.quartz.scheduler.instanceId}</prop>
                <prop key="org.quartz.scheduler.skipUpdateCheck">${org.quartz.scheduler.skipUpdateCheck}</prop>               
               
                <prop key="org.quartz.threadPool.class">${org.quartz.threadPool.class}</prop>
                <prop key="org.quartz.threadPool.threadCount">${org.quartz.threadPool.threadCount}</prop>
                <prop key="org.quartz.threadPool.threadPriority">${org.quartz.threadPool.threadPriority}</prop>
               
                <prop key="org.quartz.jobStore.misfireThreshold">${org.quartz.jobStore.misfireThreshold}</prop>
                <prop key="org.quartz.jobStore.class">${org.quartz.jobStore.class}</prop>
                <prop key="org.quartz.jobStore.driverDelegateClass">${org.quartz.jobStore.driverDelegateClass}</prop>
                <prop key="org.quartz.jobStore.useProperties">${org.quartz.jobStore.useProperties}</prop>
                <prop key="org.quartz.jobStore.dataSource">${org.quartz.jobStore.dataSource}</prop>
                <prop key="org.quartz.jobStore.tablePrefix">${org.quartz.jobStore.tablePrefix}</prop>
                <prop key="org.quartz.jobStore.isClustered">${org.quartz.jobStore.isClustered}</prop>
                <prop key="org.quartz.jobStore.clusterCheckinInterval">${org.quartz.jobStore.clusterCheckinInterval}</prop>

                <prop key="org.quartz.dataSource.jobScheduler.jndiURL">${org.quartz.dataSource.jobScheduler.jndiURL}
                </prop>
                <prop key="org.quartz.dataSource.jobScheduler.validationQuery">
${org.quartz.dataSource.jobScheduler.validationQuery}
                </prop>
            </props>
        </property>
    </bean>

Plik quartz.properties powinien wyglądać następująco:

#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = ClusteredScheduler
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 25
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = jobScheduler
org.quartz.jobStore.tablePrefix = MY_QRTZ_

org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000

#============================================================================
# Configure Datasources
#============================================================================
org.quartz.dataSource.jobScheduler.jndiURL = java:MyDS
org.quartz.dataSource.jobScheduler.validationQuery=select 0 from dual
#Konfiguracja do polaczenia bez uzycia jndiName
#org.quartz.dataSource.jobScheduler.driver = oracle.jdbc.driver.OracleDriver
#org.quartz.dataSource.jobScheduler.URL = jdbc:oracle:thin:@localhost:1521:ssid
#org.quartz.dataSource.jobScheduler.user = tp2
#org.quartz.dataSource.jobScheduler.password = tp2
#org.quartz.dataSource.jobScheduler.maxConnections = 5
#org.quartz.dataSource.jobScheduler.validationQuery=select 0 from dual

Skrypt SQL do wykonania na wskazanym schemacie bazy danych:

DELETE FROM MY_QRTZ_JOB_LISTENERS;
DELETE FROM MY_QRTZ_TRIGGER_LISTENERS;
DELETE FROM MY_QRTZ_FIRED_TRIGGERS;
DELETE FROM MY_QRTZ_SIMPLE_TRIGGERS;
DELETE FROM MY_QRTZ_CRON_TRIGGERS;
DELETE FROM MY_QRTZ_BLOB_TRIGGERS;
DELETE FROM MY_QRTZ_TRIGGERS;
DELETE FROM MY_QRTZ_JOB_DETAILS;
DELETE FROM MY_QRTZ_CALENDARS;
DELETE FROM MY_QRTZ_PAUSED_TRIGGER_GRPS;
DELETE FROM MY_QRTZ_LOCKS;
DELETE FROM MY_QRTZ_SCHEDULER_STATE;

DROP TABLE MY_QRTZ_CALENDARS;
DROP TABLE MY_QRTZ_FIRED_TRIGGERS;
DROP TABLE MY_QRTZ_TRIGGER_LISTENERS;
DROP TABLE MY_QRTZ_BLOB_TRIGGERS;
DROP TABLE MY_QRTZ_CRON_TRIGGERS;
DROP TABLE MY_QRTZ_SIMPLE_TRIGGERS;
DROP TABLE MY_QRTZ_TRIGGERS;
DROP TABLE MY_QRTZ_JOB_LISTENERS;
DROP TABLE MY_QRTZ_JOB_DETAILS;
DROP TABLE MY_QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE MY_QRTZ_LOCKS;
DROP TABLE MY_QRTZ_SCHEDULER_STATE;


CREATE TABLE MY_QRTZ_JOB_DETAILS
  (
    JOB_NAME  VARCHAR2(80) NOT NULL,
    JOB_GROUP VARCHAR2(80) NOT NULL,
    DESCRIPTION VARCHAR2(120) NULL,
    JOB_CLASS_NAME   VARCHAR2(128) NOT NULL,
    IS_DURABLE VARCHAR2(1) NOT NULL,
    IS_VOLATILE VARCHAR2(1) NOT NULL,
    IS_STATEFUL VARCHAR2(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR2(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (JOB_NAME,JOB_GROUP)
);
CREATE TABLE MY_QRTZ_JOB_LISTENERS
  (
    JOB_NAME  VARCHAR2(80) NOT NULL,
    JOB_GROUP VARCHAR2(80) NOT NULL,
    JOB_LISTENER VARCHAR2(80) NOT NULL,
    PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER),
    FOREIGN KEY (JOB_NAME,JOB_GROUP)
    REFERENCES MY_QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
);
CREATE TABLE MY_QRTZ_TRIGGERS
  (
    TRIGGER_NAME VARCHAR2(80) NOT NULL,
    TRIGGER_GROUP VARCHAR2(80) NOT NULL,
    JOB_NAME  VARCHAR2(80) NOT NULL,
    JOB_GROUP VARCHAR2(80) NOT NULL,
    IS_VOLATILE VARCHAR2(1) NOT NULL,
    DESCRIPTION VARCHAR2(120) NULL,
    NEXT_FIRE_TIME NUMBER(13) NULL,
    PREV_FIRE_TIME NUMBER(13) NULL,
    TRIGGER_STATE VARCHAR2(16) NOT NULL,
    TRIGGER_TYPE VARCHAR2(8) NOT NULL,
    START_TIME NUMBER(13) NOT NULL,
    END_TIME NUMBER(13) NULL,
    CALENDAR_NAME VARCHAR2(80) NULL,
    MISFIRE_INSTR NUMBER(2) NULL,
    JOB_DATA BLOB NULL,
    PRIORITY NUMBER(13) NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (JOB_NAME,JOB_GROUP)
    REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
);
CREATE TABLE MY_QRTZ_SIMPLE_TRIGGERS
  (
    TRIGGER_NAME VARCHAR2(80) NOT NULL,
    TRIGGER_GROUP VARCHAR2(80) NOT NULL,
    REPEAT_COUNT NUMBER(7) NOT NULL,
    REPEAT_INTERVAL NUMBER(12) NOT NULL,
    TIMES_TRIGGERED NUMBER(7) NOT NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES MY_QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE MY_QRTZ_CRON_TRIGGERS
  (
    TRIGGER_NAME VARCHAR2(80) NOT NULL,
    TRIGGER_GROUP VARCHAR2(80) NOT NULL,
    CRON_EXPRESSION VARCHAR2(80) NOT NULL,
    TIME_ZONE_ID VARCHAR2(80),
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES MY_QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE MY_QRTZ_BLOB_TRIGGERS
  (
    TRIGGER_NAME VARCHAR2(80) NOT NULL,
    TRIGGER_GROUP VARCHAR2(80) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES MY_QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE MY_QRTZ_TRIGGER_LISTENERS
  (
    TRIGGER_NAME  VARCHAR2(80) NOT NULL,
    TRIGGER_GROUP VARCHAR2(80) NOT NULL,
    TRIGGER_LISTENER VARCHAR2(80) NOT NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES MY_QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE MY_QRTZ_CALENDARS
  (
    CALENDAR_NAME  VARCHAR2(80) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (CALENDAR_NAME)
);
CREATE TABLE MY_QRTZ_PAUSED_TRIGGER_GRPS
  (
    TRIGGER_GROUP  VARCHAR2(80) NOT NULL,
    PRIMARY KEY (TRIGGER_GROUP)
);
CREATE TABLE MY_QRTZ_FIRED_TRIGGERS
  (
    ENTRY_ID VARCHAR2(95) NOT NULL,
    TRIGGER_NAME VARCHAR2(80) NOT NULL,
    TRIGGER_GROUP VARCHAR2(80) NOT NULL,
    IS_VOLATILE VARCHAR2(1) NOT NULL,
    INSTANCE_NAME VARCHAR2(80) NOT NULL,
    FIRED_TIME NUMBER(13) NOT NULL,
    STATE VARCHAR2(16) NOT NULL,
    JOB_NAME VARCHAR2(80) NULL,
    JOB_GROUP VARCHAR2(80) NULL,
    IS_STATEFUL VARCHAR2(1) NULL,
    REQUESTS_RECOVERY VARCHAR2(1) NULL,
    PRIORITY NUMBER(13) NULL,
    PRIMARY KEY (ENTRY_ID)
);
CREATE TABLE MY_QRTZ_SCHEDULER_STATE
  (
    INSTANCE_NAME VARCHAR2(80) NOT NULL,
    LAST_CHECKIN_TIME NUMBER(13) NOT NULL,
    CHECKIN_INTERVAL NUMBER(13) NOT NULL,
    RECOVERER VARCHAR2(80) NULL,
    PRIMARY KEY (INSTANCE_NAME)
);
CREATE TABLE MY_QRTZ_LOCKS
  (
    LOCK_NAME  VARCHAR2(40) NOT NULL,
    PRIMARY KEY (LOCK_NAME)
);
INSERT INTO MY_QRTZ_LOCKS VALUES('TRIGGER_ACCESS');
INSERT INTO MY_QRTZ_LOCKS VALUES('JOB_ACCESS');
INSERT INTO MY_QRTZ_LOCKS VALUES('CALENDAR_ACCESS');
INSERT INTO MY_QRTZ_LOCKS VALUES('STATE_ACCESS');
INSERT INTO MY_QRTZ_LOCKS VALUES('MISFIRE_ACCESS');

Napotkane problemy:

Wygląda na to że w wersji Camel 2.8.0 jest problem z CronScheduledRoutePolicy dla klastrowania.
Przy restarcie serwera/re-deploy aplikacji pojawia się wyjątek: org.quartz.ObjectAlreadyExistsException: Unable to store Job with name: 'job-...' and group: 'jobGroup-...', because one already exists with this identification.
Niestety na tą chwilę nie znalazłem innego sposobu jak napisanie klasy zarządzającej wskazaną trasą. Start/Stop odbywa się na zasadzie dwóch oddzielnych Quartz które podaję jako parametr. Zapewne ktoś powie że to mało eleganckie ... - nie twierdzę że to szczyt profesjonalizmu. Jednak wybrałem tą opcję po przeanalizowaniu za i przeciw.

Brak komentarzy:

Prześlij komentarz