2026年6月5日金曜日

SpringBoot + JobRunr で Web アプリとワーカープロセスを分離する方法

SpringBoot + JobRunr で Web アプリとワーカープロセスを分離する方法

概要

これまでは同じプロセス内で Web アプリと JobRunr ワーカーを起動させていました
これだとワーカーだけをスケールアウトさせたい場合に不便です
今回は Web アプリとワーカーを別々のプロセスで起動する方法を紹介します

環境

  • macOS 26.4.1
  • openjdk 26.0.1
  • SpringBoot 4.0.6
    • jasypt-spring-boot 4.0.4
    • jackson-databind 2.21.2
    • jobrunr 8.6.1
  • gradle 9.5.1
  • VSCode 1.121.0
  • MySQL 9.6.0
  • Redis 8.6.3

Web アプリケーション

Web アプリ専用のアプリケーションを作成します
こちらは SpringBoot が必要なので @SpringBootApplication にします

また専用のプロパティ (application.properties と application-web.properties) を読み込むので profiles に web を指定します

  • vim src/main/java/com/example/demo/AccessingDataMysqlApplication.java
package com.example.demo;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;

@EnableEncryptableProperties
@ConfigurationPropertiesScan
@SpringBootApplication
public class AccessingDataMysqlApplication {

        public static void main(String[] args) {
                new SpringApplicationBuilder(AccessingDataMysqlApplication.class).profiles("web").run(args);
        }

}

Web アプリケーション用設定ファイル

今回は application.properties だけを読み込むので application-web.properties は作成しなくて OK です

もし Web アプリにだけ必要な設定や application.properties の設定を上書きしたい場合には application-web.properties を作成して設定しましょう

  • vim src/main/resources/application.properties
spring.application.name=demo
# update はアプリケーション起動時に、Entityに対応するテーブルがなければ作成します
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

spring.datasource.url=jdbc:mysql://${MYSQL_HOST:127.0.0.1}:3306/mydatabase
spring.datasource.username=ENC(bFMupckSRHJ/9QmXd1EVTw==)
spring.datasource.password=ENC(QFCt95vHHcJpgm8oKhghkJoEknQv3tF0)

spring.data.redis.host=localhost
spring.data.redis.port=6379

# Web起動時はJobRunrのBackgroundJobServerを無効化(worker jarで有効化)
app.jobrunr.background-job-server.enabled=false

# Jasypt 暗号化キーの設定(環境変数から取得)
jasypt.encryptor.algorithm=PBEWithMD5AndDES
jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator
jasypt.encryptor.password=${JASYPT_PASSWORD:default_password}

ワーカーアプリケーション

@SpringBootApplication は不要です
JobRunr として動かすために SpringApplicationBuilder を使い定義します
こうすることで SpringBoot の Bean 機能などが使えます

profiles には worker を指定し application-worker.properties を使えるようにします

  • vim src/main/java/com/example/demo/WorkerApplication.java
package com.example.demo;

import org.springframework.boot.builder.SpringApplicationBuilder;
public class WorkerApplication {

        public static void main(String[] args) {
                new SpringApplicationBuilder(AccessingDataMysqlApplication.class).profiles("worker").run(args);
        }
}

ワーカーアプリケーション用設定ファイル

ワーカー専用の設定ファイルを作成します
Web アプリとして動作しないための設定を記載します
application.properties の設定を上書きします

  • vim src/main/resources/application-worker.properties
spring.main.web-application-type=none

app.jobrunr.background-job-server.enabled=true

Bean に設定を注入するためのクラス

application-worker.properties の値は自動的に読み込まれます
しかし Bean には読み込まれないのでプロパティファイルから設定を取得し Bean に注入します

  • vim src/main/java/com/example/demo/config/JobRunrBackgroundJobServerProperties.java
package com.example.demo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "app.jobrunr.background-job-server")
public class JobRunrBackgroundJobServerProperties {

        private boolean enabled = false;

        public boolean isEnabled() {
                return enabled;
        }

        public void setEnabled(boolean enabled) {
                this.enabled = enabled;
        }
}

そしてこの設定を使って JobRunr の設定 (Bean) を作成します

  • vim src/main/java/com/example/demo/config/JobRunrConfig.java
package com.example.demo.config;

import javax.sql.DataSource;

import org.jobrunr.configuration.JobRunr;
import org.jobrunr.scheduling.JobScheduler;
import org.jobrunr.server.JobActivator;
import org.jobrunr.storage.StorageProvider;
import org.jobrunr.storage.StorageProviderUtils.DatabaseOptions;
import org.jobrunr.storage.sql.mysql.MySqlStorageProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JobRunrConfig {

        @Bean
        StorageProvider storageProvider(DataSource dataSource) {
                return new MySqlStorageProvider(dataSource, DatabaseOptions.CREATE);
        }

        @Bean
        JobActivator jobActivator(ApplicationContext applicationContext) {
                return applicationContext::getBean;
        }

        @Bean
        JobScheduler jobScheduler(StorageProvider storageProvider, JobActivator jobActivator,
                        JobRunrBackgroundJobServerProperties jobRunrBackgroundJobServerProperties) {
                return JobRunr.configure().useStorageProvider(storageProvider).useJobActivator(jobActivator)
                                .useBackgroundJobServerIf(jobRunrBackgroundJobServerProperties.isEnabled()).initialize()
                                .getJobScheduler();
        }
}

サービスなどの設定

これは前回と同様です

build.gradle の修正

以下の設定を追記します
Web アプリケーションだけを起動する設定とビルドする設定、そしてワーカーアプリケーションだけを起動しビルドする設定を追加します

  • vim build.gradle
tasks.named('bootJar') {
       archiveClassifier = 'web'
       mainClass = 'com.example.demo.AccessingDataMysqlApplication'
}

tasks.named('bootRun', org.springframework.boot.gradle.tasks.run.BootRun) {
       mainClass = 'com.example.demo.AccessingDataMysqlApplication'
}

def webBootJar = tasks.named('bootJar', org.springframework.boot.gradle.tasks.bundling.BootJar)

tasks.register('workerBootRun', org.springframework.boot.gradle.tasks.run.BootRun) {
       group = 'application'
       description = 'Runs the worker process (non-web) using WorkerApplication.'
       mainClass = 'com.example.demo.WorkerApplication'
       classpath = sourceSets.main.runtimeClasspath
}

tasks.register('workerBootJar', org.springframework.boot.gradle.tasks.bundling.BootJar) {
       group = 'build'
       description = 'Builds the executable worker jar.'
       archiveClassifier = 'worker'
       mainClass = 'com.example.demo.WorkerApplication'
       classpath = sourceSets.main.runtimeClasspath
       targetJavaVersion = webBootJar.get().targetJavaVersion
       dependsOn tasks.named('classes')
}

tasks.named('assemble') {
       dependsOn tasks.named('workerBootJar')
}

動作確認

  • ./gradlew clean && ./gradlew bootRun --args='--jasypt.encryptor.password=xxx'
  • ./gradlew clean && ./gradlew workerBootRun -Dspring-boot.run.mainClass=com.example.demo.WorkerApplication --args='--jasypt.encryptor.password=xxx'

Web アプリケーション、ワーカーアプリケーションそれぞれのプロセスで立ち上げます
これで curl で動作確認するとワーカー側のロギング処理がワーカー側の起動したターミナルだけに表示されることが確認できます

ビルド

jar を作成するには以下の通りです

  • JASYPT_PASSWORD="xxx" ./gradlew clean build

これで build/libs 配下に各種 jar が作成できます
jar を実行するには以下のようにします

  • JASYPT_PASSWORD=asdfghjkl java -jar build/libs/demo-0.0.1-SNAPSHOT-web.jar
  • JASYPT_PASSWORD=asdfghjkl java -jar build/libs/demo-0.0.1-SNAPSHOT-worker.jar

最後に

JobRunr で Web アプリケーションとワーカーアプリケーションを分離させる方法を紹介しました
もしワーカーを増やしたい場合はワーカーの jar だけ追加で起動すれば OK です

試しに複数起動してみましたが2つのワーカーが処理することはなかったので大丈夫だと思います

0 件のコメント:

コメントを投稿