跨多个服务器的单个作业执行

Posted

技术标签:

【中文标题】跨多个服务器的单个作业执行【英文标题】:Single job execution across multiple servers 【发布时间】:2017-01-01 15:49:53 【问题描述】:

我已经设置了多个服务器运行@Schedule,该@Schedule 运行春季批处理作业,向用户发送电子邮件。我想确保这个作业只有一个实例在多台服务器上运行。

基于this 问题 我已经实现了一些逻辑,看看是否可以仅使用 spring 批处理来解决这个问题。

为了运行作业,我使用以下方法创建了一个辅助类 JobRunner

public void run(Job job) 
    try 
        jobLauncher.run(job, new JobParameters());
     catch (JobExecutionAlreadyRunningException e) 

        // Check if job is inactive and stop it if so.
        stopIfInactive(job);

     catch (JobExecutionException e) 
        ...
    

stopIfInactive 方法:

private void stopIfInactive(Job job) 
    for (JobExecution execution : jobExplorer.findRunningJobExecutions(job.getName())) 
        Date createTime = execution.getCreateTime();

        DateTime now = DateTime.now();

        // Get running seconds for more info.
        int seconds = Seconds
                .secondsBetween(new DateTime(createTime), now)
                .getSeconds();

        LOGGER.debug("Job '' already has an execution with id:  with age of s",
                job.getName(), execution.getId(), seconds);

        // If job start time exceeds the execution window, stop the job.
        if (createTime.before(now.minusMillis(EXECUTION_DEAD_MILLIS)
                .toDate())) 

            LOGGER.warn("Execution with id:  is inactive, stopping",
                    execution.getId());

            execution.setExitStatus(new ExitStatus(BatchStatus.FAILED.name(),
                    String.format("Stopped due to being inactive for %d seconds", seconds)));

            execution.setStatus(BatchStatus.FAILED);
            execution.setEndTime(now.toDate());

            jobRepository.update(execution);
        
    

然后作业由以下所有服务器上运行:

@Scheduled(cron = "$email.cron")
public void sendEmails() 
    jobRunner.run(emailJob);

这是多服务器设置的有效解决方案吗?如果没有,有什么替代方案?

编辑 1

我做了更多测试 - 设置两个应用程序,每 5 秒运行一次 @Schedule,使用我创建的帮助程序类启动作业。看来我的解决方案不能解决问题。这是spring批处理使用的batch_job_execution表中的数据:

 job_execution_id | version | job_instance_id |       create_time       |       start_time        |        end_time         |  status   | exit_code | exit_message |      last_updated       | job_configuration_location
------------------+---------+-----------------+-------------------------+-------------------------+-------------------------+-----------+-----------+--------------+-------------------------+----------------------------
             1007 |       2 |               2 | 2016-08-25 14:43:15.024 | 2016-08-25 14:43:15.028 | 2016-08-25 14:43:16.84  | COMPLETED | COMPLETED |              | 2016-08-25 14:43:16.84  |
             1006 |       1 |               2 | 2016-08-25 14:43:15.021 | 2016-08-25 14:43:15.025 |                         | STARTED   | UNKNOWN   |              | 2016-08-25 14:43:15.025 |
             1005 |       2 |               2 | 2016-08-25 14:43:10.326 | 2016-08-25 14:43:10.329 | 2016-08-25 14:43:12.047 | COMPLETED | COMPLETED |              | 2016-08-25 14:43:12.047 |
             1004 |       2 |               2 | 2016-08-25 14:43:10.317 | 2016-08-25 14:43:10.319 | 2016-08-25 14:43:12.03  | COMPLETED | COMPLETED |              | 2016-08-25 14:43:12.03  |
             1003 |       2 |               2 | 2016-08-25 14:43:05.017 | 2016-08-25 14:43:05.02  | 2016-08-25 14:43:06.819 | COMPLETED | COMPLETED |              | 2016-08-25 14:43:06.819 |
             1002 |       2 |               2 | 2016-08-25 14:43:05.016 | 2016-08-25 14:43:05.018 | 2016-08-25 14:43:06.811 | COMPLETED | COMPLETED |              | 2016-08-25 14:43:06.811 |
             1001 |       2 |               2 | 2016-08-25 14:43:00.038 | 2016-08-25 14:43:00.042 | 2016-08-25 14:43:01.944 | COMPLETED | COMPLETED |              | 2016-08-25 14:43:01.944 |
             1000 |       2 |               2 | 2016-08-25 14:43:00.038 | 2016-08-25 14:43:00.041 | 2016-08-25 14:43:01.922 | COMPLETED | COMPLETED |              | 2016-08-25 14:43:01.922 |
              999 |       2 |               2 | 2016-08-25 14:42:55.02  | 2016-08-25 14:42:55.024 | 2016-08-25 14:42:57.603 | COMPLETED | COMPLETED |              | 2016-08-25 14:42:57.603 |
              998 |       2 |               2 | 2016-08-25 14:42:55.02  | 2016-08-25 14:42:55.023 | 2016-08-25 14:42:57.559 | COMPLETED | COMPLETED |              | 2016-08-25 14:42:57.559 |
(10 rows)

我也试过@Palcente提供的方法,结果差不多。

【问题讨论】:

你能解释一下在上述情况下什么不起作用吗?您的意思是同时运行同一作业的多个执行? 是的,两台服务器同时启动同一个作业,即使它仍在其中一个节点上运行/启动。 【参考方案1】:

Spring Integration 的最新版本添加了一些围绕分布式锁的功能。这确实是您想要用来确保只有一台服务器触发作业(只有获得锁的服务器应该启动作业)的方法。您可以在此处的文档中阅读有关 Spring Integration 锁定功能的更多信息:http://projects.spring.io/spring-integration/

【讨论】:

我在文档或 spring-integration-samples (github.com/spring-projects/spring-integration-samples) 项目中找不到任何使用 java-config 显示此功能的示例。目前我正在考虑创建一个简单的锁表。

以上是关于跨多个服务器的单个作业执行的主要内容,如果未能解决你的问题,请参考以下文章

如何在单个 Spark 作业中调用多个 writeStream 操作?

「微服务架构」跨多个微服务的数据架构模式

一个表可以跨多个数据库共享吗

什么是工作负载限制?

如何使用弹簧批处理集成从远程服务器(主服务器)在工作服务器的多个节点上运行/执行作业?

在单个服务器上为多个 Laravel 应用程序使用 Redis 队列