分区程序 - 多线程处理Spring Batch中的许多输入文件

时间:2014-03-11 17:24:12

标签: java multithreading spring spring-batch


我有一个包含超过1m xml文件的文件夹和一个单线程步骤,它以相同的方式处理这些xml文件中的每一个 (没有连接到数据库或文件之间的任何共同点)。是否有办法使这一步更加并发,例如使用一系列文件名进行分区或将文件拆分到不同的文件夹中并使用文件夹的名称?

据我所知,MultiResourcePartitioner无法处理这种情况,因为它

  

为每个资源创建一个ExecutionContext,并将它们标记为{partition0,partition1,...,partitionN}。网格大小被忽略。

2 个答案:

答案 0 :(得分:1)

由于您已经拥有单个文件,因此需要进行分组以增加并发性。如果需要增加并发性,则增加线程数。在线程执行器中。假设您有1000个文件,并且您有内存和CPU,您可以将最大线程设置为50.因此,一次将处理50个文件。一旦文件被处理,它将需要下一组50个文件。因此执行同时运行。这是一个例子。

<bean id="kpThreadPool"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"
    destroy-method="destroy">
    <property name="maxPoolSize" value="${app.max_thread_num}" />
</bean>

<batch:step id="kp.step1" next="kp.step2">
        <batch:partition step="kp.slave"
            partitioner="multiResourcePartitioner">
            <batch:handler task-executor="kpThreadPool" />
        </batch:partition>
</batch:step>

其中app.max_thread_num = 50

答案 1 :(得分:1)

经过一些修改后,最好的结果来自自定义分区程序,它根据文件夹创建分区。为了实现这一点,上一步每100k xml文件写入文件夹 分区程序的代码(MultiResource分区程序对如何管理stepExecutions有很多帮助):

public class FolderPartitioner implements Partitioner {

    private static final Logger logger = LoggerFactory.getLogger(FolderPartitioner.class);

    private static final String DEFAULT_KEY_NAME = "fileName";

    private static final String PARTITION_KEY = "partition";

    private String folder;

    private String keyName = DEFAULT_KEY_NAME;

    /**
     * Map each partition to a subfolder of the folder property
     * {@link ExecutionContext}.
     * 
     */
    public Map<String, ExecutionContext> partition(int gridSize) {
        Map<String, ExecutionContext> map = new HashMap<String, ExecutionContext>(
                gridSize);
        int i = 0;
        File dir = new File(folder);
        File[] chunkList = dir.listFiles();
        for (File chunkStep : chunkList) {
            if (chunkStep.isDirectory()) {

                ExecutionContext context = new ExecutionContext();
                context.putString(keyName, chunkStep.getName());
                logger.info("Creating partition for folder:" + context.getString(keyName));
                map.put(PARTITION_KEY + i, context);
                i++;
            }
        }
        return map;
    }

    /**
     * The name of the key for the file name in each {@link ExecutionContext}.
     * Defaults to "fileName".
     * 
     * @param keyName
     *            the value of the key
     */
    public void setKeyName(String keyName) {
        this.keyName = keyName;
    }

    public String getFolder() {
        return folder;
    }

    /**
     * The name of the folder which contains the subfolders for spliting them to steps
     * 
     * @param folder
     */
    public void setFolder(String folder) {
        this.folder = folder;
    }

}


使用此分区程序执行时间从2小时到40分钟(!!)。