Quartz Job Scheduler - 多租户设置

时间:2015-01-08 18:39:10

标签: tomcat java-ee quartz-scheduler mariadb multi-tenant

由于我没有找到任何可以引导我灵感的东西,我想我会在这里想出来。

我最近使用Quartz Job Scheduler工作了很多,我已经在一个RESTful Java Servlet中实现了它,它应该提供一个基于Sencha ExtJS的UI来创建作业。我们将Authenticator类与DatabaseManager结合使用,它负责验证用户和所有其他特定于数据库的内容(使用Hibernate)。

由于我们想在我们开发的Java企业应用程序中使用它,我们需要为每个客户运行这个东西。我们还需要使用JobStoreTX来获取MySQL数据库上的持久数据(显然是用于集群),因此易失性RAMJobStore实现是一个禁忌。我知道官方文档,Quartz本身似乎不支持多租户实现(参见Quartz Clustering)。

我们的设置看起来有点像这样(simplfied):
- 1+生产Tomcat(服务应用逻辑)
- 1 +生产Apache(服务ExtJS前端和静态内容)
- n个数据库(每个客户一个)

添加更棘手的东西:
我们拥有特定于客户的传统模块(每个客户都有自己的应用程序托管),而更多最新模块通过客户相关访问集中托管。

在我看来,在每个客户数据库上创建与Quartz相关的表是不够的,因为我们希望坚持一个简单,直接的设置(这意味着每个客户的相同表格前缀等)不要让整个农场的Quartz部署复杂化。

我已经考虑过将它与 MariaDB MaxScale 相结合,并使用过滤器将Quartz路由到基于RegEx或类似的每个客户数据库,而Quartz只与MaxScale交谈代理,但这对我试图实现的目标来说似乎有点太多开销,我甚至不确定这是否会起作用。

有什么东西会给我多租户Quartz 吗?您能否建议一种方法可以使Quartz能够处理这个问题,因为我们需要为每个客户运行作业,但是在“一个”Tomcat(实际上是集群和负载均衡)上?或者是否有其他产品/框架/库支持开箱即用的多租户?

非常感谢任何领导,想法或帮助!

2 个答案:

答案 0 :(得分:0)

我终于把它写到多个数据库了。

我所做的是根据各自的客户参数为每个客户即时生成Quartz属性文件,并使程序逻辑读取这个新生成的文件。最后,每个客户都有自己的Quartz作业存储到自己的数据库中。

答案 1 :(得分:0)

您可以使用名为 JobDataMap 的东西,它只是映射您可以用作每个作业的数据容器的内容。您需要做的是:

  • 创建新作业时,添加一些租户标识符
  • 创建 globalJobListener,您可以在其中将该映射中的租户标识符注入您的 tenantContext

这是我的代码:

// quartz configuration with custom job listener
@Configuration
public class MultiTenantQuartzAutoConfiguration {

    @Bean
    public SchedulerFactoryBean schedulerFactory() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(jobFactory());
        schedulerFactoryBean.setGlobalJobListeners(new MultiTenantQuartzJobListener());

        return schedulerFactoryBean;
    }

    @Bean
    public SpringBeanJobFactory jobFactory() {
        return new SpringBeanJobFactory();
    }
}

// custom job listener
public class MultiTenantQuartzJobListener implements JobListener {

    private static final String NAME = "tenantContext";

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        TenantContext.setTenantId(context.getJobDetail().getJobDataMap().getString(TenantContext.TENANT_HEADER));
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        TenantContext.clear();
    }

    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        TenantContext.clear();
    }
}

// and here is how to create job with filled JobDataMap
Class c = Class.forName(jobInfo.getClassName());

JobDetail job = newJob(c)
        .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
        .withDescription(jobInfo.getDesc())
        .usingJobData(TenantContext.TENANT_HEADER, TenantContext.getTenantId())
        .build();

CronTrigger trigger = newTrigger()
        .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
        .withSchedule(cronSchedule(jobInfo.getCronExpression()))
        .build();

var ft = scheduler.scheduleJob(job, trigger);
相关问题