【问题标题】:Quartz Job Scheduler - Multi-Tenant SetupQuartz Job Scheduler - 多租户设置
【发布时间】:2015-01-08 18:39:09
【问题描述】:

由于我没有找到任何相关的东西来激发我的灵感,所以我想我会在这里想出这个。

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

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

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

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

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

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

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

非常感谢任何线索、想法或帮助!

【问题讨论】:

  • 我们现在只处于开发阶段,但现在我们选择在每个客户数据库上创建与 Quartz 相关的表。如果需要,您可以通过石英属性文件更改石英表名称(因此它们将为每个客户等具有相同的表前缀)。
  • 我想我找到了一个可以轻松应用于我们的软件架构的解决方案。由于每个客户都有自己的数据库,因此在整个应用程序逻辑中都有一个可用的系统参数,这对每个客户都是唯一的。此参数将在创建时自动附加到作业和组名称。这样,我们应该能够为所有客户在单个数据库上运行 Quartz。
  • 目前工作顺利。只是一个问题:从我设计的界面(使用 Sencha ExtJS)创建工作,用户可以选择创建一次性工作或重复工作。当我创建一次性作业时,作业(和触发器)在执行后不会保留在数据库中。有没有办法保留已经解雇且不会在数据库中重复出现的工作?
  • 我认为如果不再出现该作业,它将始终删除该作业。如果你想保存它以供用户重用,那么我们所做的就是将调度数据保存在一个单独的表中。如果你想记录,也许this 对你有用。
  • 已修复。 Job 类有一个@PersistJobDataAfterExecution 注释。无论如何,谢谢。

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


【解决方案1】:

您可以使用名为 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);

【讨论】:

    【解决方案2】:

    我终于让它写入多个数据库。

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

    【讨论】:

    • 在这个解决方案中,每个客户都有一个石英调度程序,不是吗?因为我从未听说过石英支持多个数据源。谢谢你的回答。
    • 是的,没错。尽管花了一些时间来弄清楚如何为每个租户动态创建配置。不过,您需要弄清楚是什么让租户“独一无二”..
    • 你好@MoshPit,你实现了真的很棒。我想知道,你是如何实现的?您能否详细解释一下或从您的代码中提供一些代码示例。这对我真的很有帮助。提前致谢。
    • @MoshPit 您能否与我们分享您是如何实现这一目标的信息。我有一个用例,我们在同一个数据库中有多个模式,每个模式都应该有自己的调度程序
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 2022-01-13
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多