2012-02-02 4 views
4

У меня есть Spring-Batch, которое я запускаю с контроллера Spring MVC. Контроллер получает загруженный файл от пользователя, и работа должна обработать файл:Spring Batch: запуск задания из контрллера Spring MVC С НОВЫМ РЕЗЬБОЙ

@RequestMapping(value = "/upload") 
public ModelAndView uploadInventory(UploadFile uploadFile, BindingResult bindingResult) { 

    // code for saving the uploaded file to disk goes here... 

    // now I want to launch the job of reading the file line by line and saving it to the database, 
    // but I want to launch this job in a new thread, not in the HTTP request thread, 
    // since I so not want the user to wait until the job ends. 
    jobLauncher.run(
        jobRegistry.getJob(JOB_NAME), 
        new JobParametersBuilder().addString("targetDirectory", folderPath).addString("targetFile", fileName).toJobParameters() 
        ); 

    return mav; 
} 

Я попытался следующий XML конфигурации:

<job id="writeProductsJob" xmlns="http://www.springframework.org/schema/batch"> 
    <step id="readWrite"> 
     <tasklet task-executor="taskExecutor"> 
      <chunk reader="productItemReader" writer="productItemWriter" commit-interval="10" /> 
     </tasklet> 
    </step> 
</job> 

<bean id="taskExecutor" 
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> 
    <property name="corePoolSize" value="5" /> 
    <property name="maxPoolSize" value="5" /> 
</bean> 

... но кажется, что многопоточность происходит только внутри самих границ работы. I.e., поток контроллера ожидает завершения задания, а выполнение задания обрабатывается несколькими потоками (что хорошо, но не главное, что я хотел). Главное, что я хотел, так это то, что задание будет запущено в отдельном потоке (или потоках), в то время как поток контроллера продолжит выполнение, не дожидаясь окончания потоков задач.

Есть ли способ достичь этого с помощью Spring-batch?

ответ

6

Официальный documentation описывает ваши точные проблемы и решение в 4.5.2. Running Jobs from within a Web Container:

[...] Контроллер запускает работу с помощью JobLauncher, который был настроен для запуска асинхронно, который немедленно возвращает JobExecution. Работа, скорее всего, будет выполняться, однако это неблокирующее поведение позволяет контроллеру немедленно вернуться, что требуется при обработке HttpRequest.

Spring Batch http://static.springsource.org/spring-batch/reference/html-single/images/launch-from-request.png

Таким образом, вы были довольно близки в попытке использовать TaskExecutor, однако оно должно быть передано JobLauncher вместо:

<bean id="jobLauncher" 
     class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> 
    <property name="jobRepository" ref="jobRepository" /> 
    <property name="taskExecutor" ref="taskExecutor"/> 
</bean> 

Отказ от ответственности: Я никогда не использовал Spring Пакет ...

+0

Спасибо! именно то, что я хотел! новичка начинающего :) – rapt

+0

Вторая ссылка - 404, пожалуйста, уточните правильную ссылку. – Rembo

1

Если вам не нужно отображать ошибки обработки для вашего клиента, вы можете запустить весеннее пакетное задание в отдельном потоке.

+0

Я думал об этом - но есть ли условия для этого в весеннем режиме? Например. предположим, что я хочу, чтобы каждый запрос запускал новый поток для своей работы, но только до 10 таких потоков - если в настоящее время мне приходится обрабатывать более 10 файлов, а только десять будут получать свой собственный поток и другие файлы будет обрабатываться позже, может быть, каким-то глобальным планировщиком. Разве это не то, что разработчики весна-партии уже реализованы (по крайней мере частично)? – rapt

3

jobLauncher.run() метод может быть вызван в новом Thread как так:

@RequestMapping(value = "/upload") 
public ModelAndView uploadInventory(UploadFile uploadFile, BindingResult bindingResult) { 
    [...] 

    final SomeObject jobLauncher = [...] 
    Thread thread = new Thread(){ 
    @Override 
    public void run(){ 
     jobLauncher.run([...]); 
    } 
    }; 
    thread.start(); 

    return mav; 
} 

thread.start() линия будет порождать новый поток, а затем продолжить выполнение кода ниже него.

Обратите внимание, что если jobLauncher является локальной переменной, ее необходимо объявить final, чтобы она использовалась внутри анонимного класса Thread.