使用Spring批处理同时读取和写入多个文件

时间:2015-04-24 10:25:13

标签: spring spring-batch

我们正在开发一个将读取多个文件的应用程序。写入多个文件,即一个输入文件的一个输出文件(输出文件的名称必须与输入文件相同)。 MultiResourceItemReader可以读取多个文件但不能同时读取,这对我们来说是一个性能瓶颈。 Spring批处理为此提供了多线程支持,但是许多线程将再次读取同一文件&试着写下来。由于输出文件名必须与输入文件名相同,因此我们也不能使用该选项。

现在我正在寻找一种可能性,如果我可以创建'n'线程来阅读&写'n'个文件。但我不确定如何将此逻辑与Spring Batch框架集成。

感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

由于MultiResourceItemReader无法满足您的性能需求,您可以仔细查看parallel processing,您已经提到过这是一个理想的选项。我不认为很多线程会读取同一个文件并尝试在运行多线程时编写它,如果配置正确的话。

您可以创建一个分区(多线程)的面向任务的步骤,而不是采用典型的面向块的方法。 tasklet类将成为主要驱动程序,将调用委托给读者和编写者。

总体流程将是这样的:

  1. 检索需要读取/写出的所有文件的名称(通过某些服务类),并将它们保存到Partitioner实现中的执行上下文中。

    public class filePartitioner implements Partitioner {
    
    @Override
    public Map<String, ExecutionContext> partition(int gridSize) {
        Map<String, Path> filesToProcess = this.service.getFilesToProcess(directory); // this is just sudo-ish code but maybe you inject the directory you'll be targeting into this class
        Map<String, ExecutionContext> execCtxs = new HashMap<>();
        for(Entry<String, Path> entry : filesToProcess.entrySet()) {
            ExecutionContext execCtx = new ExecutionContext();
            execCtx.put("file", entry.getValue());
            execCtxs.put(entry.getKey(), execCtx);  
        }
    
        return execCtxs;
    }
    
    // injected
    public void setServiceClass(ServiceClass service) {
        this.service = service;
    }
    }
    

    一个。对于.getFilesToProcess()方法,您只需要返回指定目录中的所有文件,因为您最终需要知道要读取的内容以及要写入的文件的名称。显然,有几种方法可以解决这个问题,例如......

    public Map<String, Path> getFilesToProcess(String directory) {
        Map<String, Path> filesToProcess = new HashMap<String, Path>();
        File directoryFile = new File(directory); // where directory is where you intend to read from
        this.generateFileList(filesToProcess, directoryFile, directory);
    
    private void generateFileList(Map<String, Path> fileList, File node, String directory) {
        // traverse directory and get files, adding to file list.
    
        if(node.isFile()) {
            String file = node.getAbsoluteFile().toString().substring(directory.length() + 1, node.toString().length());
            fileList.put(file, directory);
    }
    
        if(node.isDirectory()) {
            String[] files = node.list();
            for(String filename : files) {
                this.generateFileList(fileList, new File(node, filename), directory);
            }
        }
    }
    
  2. 您需要创建一个tasklet,它将从执行上下文中提取文件名,并将它们传递给一些注入的类,该类将读入该文件并将其写出(可能需要自定义ItemReaders和ItemWriters)

  3. 剩下的工作将是配置,这应该是相当直接的。在分区程序的配置中,您可以设置网格大小,如果您真的打算为n个文件创建n个线程,甚至可以使用SpEL动态完成。我敢打赌,在n个文件中运行的固定数量的线程会显示出性能方面的显着改善,但您将能够自己确定。

  4. 希望这有帮助。