【问题标题】:Reading and writing multiple files simultaneously using Spring batch使用Spring批处理同时读写多个文件
【发布时间】:2015-04-24 10:25:13
【问题描述】:

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

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

提前感谢您的帮助。

【问题讨论】:

    标签: spring spring-batch


    【解决方案1】:

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

    您可以创建一个分区(多线程)的面向 tasklet 的步骤,而不是采用典型的面向块的方法。 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. 其余的工作将在配置中,这应该是相当简单的。在 Partitioner 的配置中,您可以设置网格大小,如果您真的打算为 n 个文件创建 n 个线程,甚至可以使用 SpEL 动态完成。我敢打赌,在 n 个文件中运行固定数量的线程会显着提高性能,但您可以自己确定。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2020-06-15
      • 2015-06-03
      • 2012-07-30
      • 2019-04-01
      • 2022-07-04
      • 2019-03-16
      • 1970-01-01
      • 1970-01-01
      • 2017-02-18
      相关资源
      最近更新 更多