Apache CamelフレームワークのQuartzコンポーネントでクラスタリングをやってみた

# はじめに

Camelフレームワークには、標準でジョブスケジューラライブラリのQuartz2コンポーネントがあり、簡単にジョブの定期実行ができます。


GitHubにQuartzコンポーネントでクラスタリングをやっているプロジェクトを見つけたのですが、Quartz1のものだったため、これをベースにQuartz2コンポーネントでクラスタリングを実施してみました。

https://github.com/yashpatil/camel-quartz-cluster

# PostgreSQLにQuartz用のテーブルを作成する。

クラスタリングのため、DBにQuartz用のテーブルを作成します。
様々なDBが対応していますが、今回はPostgreSQLを使用しています。

DDLは、quartz-2.2.3をダウンロードし、以下のファイルを用います。

quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables\tables_postgres.sql

サーバにログインし、以下のコマンドを実行します。

データベースquartzとユーザquartzを作成する。

```
# psql -U postgres

postgres=# create database quartz;
CREATE DATABASE
postgres=# CREATE USER quartz WITH PASSWORD 'quartz';
CREATE ROLE
postgres=# CREATE SCHEMA quartz AUTHORIZATION quartz;
CREATE SCHEMA
```

データベースquartzにQuartz用のテーブルを作成する。

```
postgres=> \c quartz
You are now connected to database "quartz" as user "quartz".

quartz=> \i /tmp/tables_postgres.sql
～～
CREATE INDEX
psql:/tmp/tables_postgres.sql:187: WARNING:  there is no transaction in progress
COMMIT
```

テーブルが作成されたことを確認する。

```
quartz=> \d
                 List of relations
 Schema |           Name           | Type  | Owner  
--------+--------------------------+-------+--------
 public | qrtz_blob_triggers       | table | quartz
 public | qrtz_calendars           | table | quartz
 public | qrtz_cron_triggers       | table | quartz
 public | qrtz_fired_triggers      | table | quartz
 public | qrtz_job_details         | table | quartz
 public | qrtz_locks               | table | quartz
 public | qrtz_paused_trigger_grps | table | quartz
 public | qrtz_scheduler_state     | table | quartz
 public | qrtz_simple_triggers     | table | quartz
 public | qrtz_simprop_triggers    | table | quartz
 public | qrtz_triggers            | table | quartz
(11 rows)

```

# Camelの設定ファイルを作成する。

10秒ごとに実行される以下の3つのジョブを作成しています。
上の2つはクラスタリングして、最後のジョブはクラスタ対象外です。

\<from uri="quartz2cluster://test/testTimer?job.name=demoQuartzCluster&amp;cron=0/10+*+*+*+*+?" />

\<from uri="quartz2cluster://test/test2Timer?job.name=demoQuartzCluster2&amp;cron=0/10+*+*+*+*+?" />

\<from uri="quartz2://test/test3Timer?cron=0/10+*+*+*+*+?" />


・1インスタンスで実行すると3つのジョブが実行されました。
・もう1インスタンス追加すると、2つ目のジョブが追加したインスタンスで実行されました。
　つまり、クラスタリングしているジョブはいずれかのインスタンスで実行されたということです。
・クラスタ対象外のジョブは両方のインスタンスで実行されました。
・1つ目のインスタンスを停止させると、実行されていたジョブが2つ目のインスタンスで実行されるようになりました。

一応、うまくクラスタリングされているようです。色々と設定があるので、もう少し調査は必要ですが。
あと、試していませんがquartz.propertiesを使用して設定もできるようです。


以下、使用したcamelコンテキストのxmlファイルです。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
          http://camel.apache.org/schema/spring
          http://camel.apache.org/schema/spring/camel-spring.xsd
          http://www.springframework.org/schema/util
          http://www.springframework.org/schema/util/spring-util-4.2.xsd">

	<bean id="quartzDataSource" class="org.apache.commons.dbcp2.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="org.postgresql.Driver" />
		<property name="url" value="jdbc:postgresql://192.168.10.71:5432/quartz" />
		<property name="username" value="quartz" />
		<property name="password" value="quartz" />
	</bean>

	<bean id="quartz2cluster" class="org.apache.camel.component.quartz2.QuartzComponent">
		<property name="scheduler" ref="scheduler" />
		<property name="startDelayedSeconds" value="60" />
	</bean>

	<bean id="scheduler"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="dataSource" ref="quartzDataSource" />
		<property name="autoStartup" value="false" />

		<property name="schedulerContextAsMap">
			<map>
				<entry key="CamelQuartzCamelContext" value-ref="quartzCluster" />
			</map>
		</property>
		<property name="quartzProperties">
			<props>
				<prop key="org.quartz.scheduler.instanceName">myscheduler</prop>
				<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
				<prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>
				<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
				</prop>
				<prop key="org.quartz.jobStore.isClustered">true</prop>
				<prop key="org.quartz.jobStore.clusterCheckinInterval">5000</prop>
			</props>
		</property>
	</bean>

	<camelContext xmlns="http://camel.apache.org/schema/spring"
		id="quartzCluster">
		<route id="quartzClusterTest1">
			<from
				uri="quartz2cluster://test/testTimer?job.name=demoQuartzCluster&amp;cron=0/10+*+*+*+*+?" />
			<log message="quartzClusterTest end" />
		</route>
		<route id="quartzClusterTest2">
			<from
				uri="quartz2cluster://test/test2Timer?job.name=demoQuartzCluster2&amp;cron=0/10+*+*+*+*+?" />
			<log message="quartzClusterTes2 end" />
		</route>
		<route id="quartzTest">
			<from
				uri="quartz2://test/test3Timer?cron=0/10+*+*+*+*+?" />
			<log message="quartzTest end" />
		</route>

	</camelContext>
</beans>
```

ちなみにpom.xmlは以下のとおり。

```pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>sample</groupId>
	<artifactId>camel_quartz</artifactId>
	<version>1.0.0</version>
	<name>camel_quartz</name>
	<description>camel_quartz</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<camel.version>2.22.0</camel.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-core</artifactId>
			<version>${camel.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-spring</artifactId>
			<version>${camel.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-quartz2</artifactId>
			<version>${camel.version}</version>
		</dependency>
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.5.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>5.0.9.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.0.9.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<version>42.2.5</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>2.8.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.8.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-slf4j-impl</artifactId>
			<version>2.8.2</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
	</dependencies>
</project>
```


# 参考

以下を参考にしました。

yashpatil/camel-quartz-cluster
https://github.com/yashpatil/camel-quartz-cluster/tree/master/src/main/resources/META-INF/spring

Quartz2 Component
https://github.com/apache/camel/blob/master/components/camel-quartz2/src/main/docs/quartz2-component.adoc

Quartz JDBC storeを使ってJOB管理(クラスタリングもやってみました）
https://qiita.com/uzresk/items/8a4cdbdbbfec67bcf658