Spring Boot API-并行处理文档并在文档上执行python脚本 - java

场景:

在我的应用程序中,有3个进程正在其各自文件夹中的共享驱动器上复制文档。
一旦将任何文档复制到共享驱动器上(通过任何进程),目录观察器(Java)代码就会拾取该文档并使用“ Process”调用Python脚本,并对文档进行一些处理。代码段如下:

Process pr = Runtime.getRuntime().exec(pythonCommand);
            // retrieve output from python script
            BufferedReader bfr = new BufferedReader(new InputStreamReader(pr.getInputStream()));
            String line = "";
            while ((line = bfr.readLine()) != null) {
                // display each output line from python script
                logger.info(line);
            }
            pr.waitFor();

目前,我的代码一直等到该文档上的python代码执行完成。只有在此之后,它才会拾取下一个文档。 Python代码需要30秒才能完成。
处理完文档后,文档将从当前文件夹移至存档或错误文件夹。
请在下面的场景屏幕截图中找到:
Spring Boot API-并行处理文档并在文档上执行python脚本 - java

问题是什么?

我的代码是以顺序方式处理文档的,因此我需要并行处理文档。
由于Python代码大约需要30秒,因此目录观察程序创建的某些事件也将丢失。
如果短时间内要发送约400个文档,则文档处理将停止。

我在找什么?

用于并行处理文档的设计解决方案。
如果出现文档处理失败的情况,则必须自动处理待处理的文档。
我也尝试了Spring Boot调度,但是仍然仅按顺序处理文档。
是否可以作为后台进程并行调用Python代码。

很长的问题很抱歉,但是我在很多天都被这个问题困扰,并且已经看过很多类似的问题。
谢谢!

参考方案

一种选择是使用JDK提供的ExecutorService,它可以执行RunnableCallable任务。您将需要创建一个实现Runnable的类,该类将执行您的Python脚本,并且在收到新文档后,您需要创建该类的新实例并将其传递给ExecutorService

为了说明它是如何工作的,我们将使用一个简单的Python脚本,该脚本以线程名作为参数,打印其执行的开始时间,休眠10秒并显示结束时间:

import time
import sys

print "%s start : %s" % (sys.argv[1], time.ctime())
time.sleep(10)
print "%s end : %s" % (sys.argv[1], time.ctime())

首先,我们实现运行脚本的类,并将在构造函数中获得的名称传递给它:

class ScriptRunner implements Runnable {

    private String thread;

    ScriptRunner(String thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        try {
            ProcessBuilder ps = new ProcessBuilder("py", "test.py", thread);
            ps.redirectErrorStream(true);
            Process pr = ps.start();
            try (BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream()))) {
                String line;
                while ((line = in.readLine()) != null) {
                    System.out.println(line);
                }
            }
            pr.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后,我们创建main方法,该方法使用固定数量的5个并行线程创建ExecutorService,并以1秒的中断将10个ScriptRunner实例传递给它:

public static void main(String[] args) throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(5);
    for (int i = 1; i <= 10; i++) {
        executor.submit(new ScriptRunner("Thread_" + i));
        Thread.sleep(1000);
    }
    executor.shutdown();
}

如果运行此方法,我们将看到由于指定的限制,该服务最多具有5个并行运行的任务,其余任务进入队列并在释放的线程中启动:

Thread_1 start : Sat Nov 23 11:40:14 2019
Thread_1 end : Sat Nov 23 11:40:24 2019    // the first task is completed..
Thread_2 start : Sat Nov 23 11:40:15 2019
...
Thread_5 end : Sat Nov 23 11:40:28 2019
Thread_6 start : Sat Nov 23 11:40:24 2019  // ..and the sixth is started
...
Thread_10 end : Sat Nov 23 11:40:38 2019

Spring Boot-使用上下文路径时在根级别的静态内容 - java

假设我有一个application.yml内容server: port: 8000 context-path: /rest 因此,将像这样访问所有控制器和htmlhttp://server:8000/rest/controller因此,具有此配置...有可能在不更改其上下文路径的情况下将一些静态html元素添加到其根级别? (我已经将index.html添加…

Spring Boot:java.time.Duration的默认序列化从字符串更改为数字 - java

我们最近从Spring Boot 2.1.9升级到2.2.1,这导致我们的测试失败。调查导致结果,默认情况下java.time.Duration类型现在序列化为不同的序列。现在,我们将得到"PT15M",而不是在JSON消息中包含字符串"900.0"。 POJO定义如下所示@JsonProperty(required …

Spring Boot-如何将application.yml属性定义为application.properties - java

我目前正在尝试使用我的Spring Boot Web应用程序设置s3存储桶以添加/删除图像。我遵循的指南使用以下application.yml属性:amazonProperties: endpointUrl: https://s3.us-east-2.amazonaws.com accessKey: XXXXXXXXXXXXXXXXX secretKey: …

Java:“自动装配”继承与依赖注入 - java

Improve this question 我通常以常见的简单形式使用Spring框架: 控制器服务存储库通常,我会在CommonService类中放一个通用服务,并使所有其他服务扩展到类中。一个开发人员告诉我,最好在每个服务中插入CommonClass而不是使用继承。我的问题是,有一个方法比另一个更好吗? JVM或性能是否会受到另一个影响?更新资料Comm…

有什么方法可以使用Spring Boot,Liquibase和sql脚本进行集成测试吗? - java

我使用liquibase设置数据库模式。我禁用休眠以创建任何东西。因此,我的import.sql被忽略。在liquibase创建表之后,有什么方法可以配置spring boot或liquibase或其他任何部分来加载测试数据? 参考方案 如果您需要一些粗糙的东西(即,不是用于实际的数据迁移),则可以使用Spring's JDBC initializ…