我们正在开发一个将读取多个文件的应用程序。写入多个文件,即一个输入文件的一个输出文件(输出文件的名称必须与输入文件相同)。 MultiResourceItemReader可以读取多个文件但不能同时读取,这对我们来说是一个性能瓶颈。 Spring批处理为此提供了多线程支持,但是许多线程将再次读取同一文件&试着写下来。由于输出文件名必须与输入文件名相同,因此我们也不能使用该选项。
现在我正在寻找一种可能性,如果我可以创建'n'线程来阅读&写'n'个文件。但我不确定如何将此逻辑与Spring Batch框架集成。
感谢任何帮助。
答案 0 :(得分:1)
由于MultiResourceItemReader无法满足您的性能需求,您可以仔细查看parallel processing,您已经提到过这是一个理想的选项。我不认为很多线程会读取同一个文件并尝试在运行多线程时编写它,如果配置正确的话。
您可以创建一个分区(多线程)的面向任务的步骤,而不是采用典型的面向块的方法。 tasklet类将成为主要驱动程序,将调用委托给读者和编写者。
总体流程将是这样的:
检索需要读取/写出的所有文件的名称(通过某些服务类),并将它们保存到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);
}
}
}
您需要创建一个tasklet,它将从执行上下文中提取文件名,并将它们传递给一些注入的类,该类将读入该文件并将其写出(可能需要自定义ItemReaders和ItemWriters)
剩下的工作将是配置,这应该是相当直接的。在分区程序的配置中,您可以设置网格大小,如果您真的打算为n个文件创建n个线程,甚至可以使用SpEL动态完成。我敢打赌,在n个文件中运行的固定数量的线程会显示出性能方面的显着改善,但您将能够自己确定。
希望这有帮助。