Spring boot下使用Quartz--多实例解决方案

公司项目在发到生产后启了2个实例,导致调度同时执行,起先使用了事务隔离和数据库控制执行版本,不过不是很好,不是最佳的解决方案,查了资料Quartz本身支持多实例,稍加配置即可。
此篇文章在Spring boot下使用Quartz 基础上进行修改,建议阅读完之后再看
1. 下载quartz
官网
我用的是2.2.3

**2. 创建表 **
下载下来tar包后解压后打开
quartz-2.2.3\docs\dbTables
找到你需要的数据库sql文件执行
例如:我用的是PG,执行的是tables_postgres.sql文件里的sql

注意:2.3.0版本sql文件在quartz-2.3.0-SNAPSHOT\src\org\quartz\impl\jdbcjobstore

3. 新增配置文件

import java.util.Properties;
import javax.sql.DataSource;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
@AutoConfigureAfter(DruidAutoConfiguration.class)//在该类之后执行,为了使用dataSource,(DruidAutoConfiguration是连接数据库配置文件,参考文末)
public class QuartzConfig {
	@Autowired
	private DataSource dataSource;
	/**
	 * 设置属性
	 */
	private Properties quartzProperties() {
		Properties prop = new Properties();
		//拉取trigger加锁
		prop.put("org.quartz.jobStore.acquireTriggersWithinLock", "true");
		prop.put("org.quartz.scheduler.instanceName", "quartzScheduler");
		prop.put("org.quartz.scheduler.instanceId", "AUTO");
		prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
		prop.put("org.quartz.scheduler.jmx.export", "true");
		
		// Configure JobStore
		// #JobDataMaps是否都为String类型
		// prop.put("org.quartz.jobStore.useProperties", "true");//不要用,报错
		prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
		// PG专用
		prop.put("org.quartz.jobStore.driverDelegateClass",
				"org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
		// job注入datasource
		// prop.put("org.quartz.jobStore.dataSource","druid");//#使用JNDI数据源的时候,数据源的名字
		prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
		
		// 开启集群
		// #ture则此实例需要参加到集群中
		prop.put("org.quartz.jobStore.isClustered", "true");
		// #调度实例失效的检查时间间隔
		prop.put("org.quartz.jobStore.clusterCheckinInterval", "20000");
		// prop.put("org.quartz.jobStore.dataSource", "myDS");
		// 这是 JobStore 能处理的错过触发的 Trigger 的最大数量
		prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
		// #容许的最大作业延长时间
		prop.put("org.quartz.jobStore.misfireThreshold", "60000");
		//值为 true 时告知 Quartz(当使用 JobStoreTX 或 CMT) 
		//调用 JDBC 连接的 setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) 方法
		//这有助于阻止某些数据库在高负载和长时间事物时锁的超时。
		prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
		prop.put("org.quartz.jobStore.selectWithLockSQL",
				"SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE");
		
		// Configure ThreadPool
		prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
		prop.put("org.quartz.threadPool.threadCount", "50");
		prop.put("org.quartz.threadPool.threadPriority", "5");
		prop.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread",
				"true");
		/*
		 * prop.put("org.quartz.dataSource.myDS.driver", myDSDriver);
		 * prop.put("org.quartz.dataSource.myDS.URL", myDSURL);
		 * prop.put("org.quartz.dataSource.myDS.user", myDSUser);
		 * prop.put("org.quartz.dataSource.myDS.password", myDSPassword);
		 * System.out.println("myDSMaxConnections:" + myDSMaxConnections);
		 * prop.put("org.quartz.dataSource.myDS.maxConnections",
		 * myDSMaxConnections);
		 */
		prop.put("org.quartz.plugin.triggHistory.class",
				"org.quartz.plugins.history.LoggingJobHistoryPlugin");
		prop.put("org.quartz.plugin.shutdownhook.class",
				"org.quartz.plugins.management.ShutdownHookPlugin");
		prop.put("org.quartz.plugin.shutdownhook.cleanShutdown", "true");
		return prop;
	}

	@Bean
	public SchedulerFactoryBean schedulerFactoryBean() {//得到schedulerFactoryBean,并注入多实例参数
		SchedulerFactoryBean factory = new SchedulerFactoryBean();
		// this allows to update triggers in DB when updating settings in config
		// file:
		// 用于quartz集群,QuartzScheduler
		// 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
		factory.setOverwriteExistingJobs(true);
		// 用于quartz集群,加载quartz数据源
		factory.setDataSource(dataSource);
		// QuartzScheduler 延时启动,应用启动完10秒后 QuartzScheduler 再启动
		factory.setStartupDelay(10);
		// 用于quartz集群,加载quartz数据源配置
		factory.setQuartzProperties(quartzProperties());
		factory.setAutoStartup(true);
		// applicationContextSchedulerContextKey:
		// 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下
		// 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文
		// factory.setApplicationContextSchedulerContextKey("applicationContext");
		// factory.setConfigLocation(new
		// FileSystemResource(this.getClass().getResource("/quartz.properties").getPath()));
		return factory;
	}

    //得到scheduler
	@Bean
	public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
		return schedulerFactoryBean.getScheduler();
	}
}

4. 修改ServiceImpl实现类的scheduler注入方式
一些调度器操作

@Service
@Slf4j
public class SchedulerServiceImpl implements SchedulerService {

	@Autowired
	private  Scheduler scheduler;
	
	内容省略,跟上一遍文章一样。
}

文末. (仅供参考)
连接数据库的配置文件
DruidAutoConfiguration.java

import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;

@Configuration
@EnableConfigurationProperties(DruidProperties.class)
@ConditionalOnClass(DruidDataSource.class)
@ConditionalOnProperty(prefix = "druid", name = "url")
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class DruidAutoConfiguration {

    @Autowired
    private DruidProperties properties;

    @Bean
    public DataSource dataSource() {
    	
    	System.out.println(DataConverterUtil.object2Json(properties));
    	System.out.println(DataConverterUtil.object2Json(properties));
    	System.out.println(DataConverterUtil.object2Json(properties));
    	System.out.println(DataConverterUtil.object2Json(properties));
    	
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(properties.getUrl());
        dataSource.setUsername(properties.getUsername());
        dataSource.setPassword(properties.getPassword());
        if (properties.getInitialSize() > 0) {
            dataSource.setInitialSize(properties.getInitialSize());
        }
        if (properties.getMinIdle() > 0) {
            dataSource.setMinIdle(properties.getMinIdle());
        }
        if (properties.getMaxActive() > 0) {
            dataSource.setMaxActive(properties.getMaxActive());
        }
        dataSource.setTestOnBorrow(properties.isTestOnBorrow());
        try {
            dataSource.init();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return dataSource;
    }
}

DruidProperties .java

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

@ConfigurationProperties(prefix = "druid")
public class DruidProperties {
    private String url;
    private String username;
    private String password;
    private String driverClass;

    private int     maxActive;
    private int     minIdle;
    private int     initialSize;
    private boolean testOnBorrow;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDriverClass() {
        return driverClass;
    }

    public void setDriverClass(String driverClass) {
        this.driverClass = driverClass;
    }

    public int getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
    }

    public int getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public int getInitialSize() {
        return initialSize;
    }

    public void setInitialSize(int initialSize) {
        this.initialSize = initialSize;
    }

    public boolean isTestOnBorrow() {
        return testOnBorrow;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }
}

application.properties

## 需要修改数据库连接信息
druid.url=jdbc:postgresql://11.11.11.11:7443/test
druid.driver-class=org.postgresql.Driver
druid.username=testdata
druid.password=yang1234
druid.initial-size=1
druid.min-idle=1
druid.max-active=20
druid.test-on-borrow=true

喜欢请点赞,不懂请留言^^

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值