Spring Boot Freemarker: как запустить длительный процесс выполнения без зависания страницы?

#java #multithreading #spring-boot #freemarker

#java #многопоточность #spring-boot #freemarker

Вопрос:

У меня есть эти HTML-формы на index.htm :

 <form action="/generate/report1" method="get">
    <input name="name" type="text" value="test"/>
    <input type="submit" value="generate report1">
</form>
<form action="/generate/report2" method="get">
    <input name="name" type="text" value="test"/>
    <input type="submit" value="generate report2">
</form>
<form action="/generate/report3" method="get">
    <input name="name" type="text" value="test"/>
    <input type="submit" value="generate report3">
</form>
  

И в контроллере у меня есть 3 метода для создания этих отчетов

 @RequestMapping(value = "/generate/report1", method = RequestMethod.GET)
public ModelAndView generate(@RequestParam String name) throws NurException, IOException {
    ModelAndView index = new ModelAndView("index");
    billService.createReport1(name);
    index.addObject("message", "Success");
    return index;
}

@RequestMapping(value = "/generate/report2", method = RequestMethod.GET)
public ModelAndView generateCorrect(@RequestParam String name) throws NurException, IOException {
    ModelAndView index = new ModelAndView("index");
    billService.createReport2(name);
    index.addObject("message", "Success");
    return index;
}

@RequestMapping(value = "/generate/report3", method = RequestMethod.GET)
public ModelAndView generateTmc(@RequestParam String name) throws NurException, IOException {
    ModelAndView index = new ModelAndView("index");
    billService.createReport3(name);
    index.addObject("message", "Success");
    return index;
}
  

Каждый метод billService генерирует отчеты и сохраняет их в формате PDF. Это очень длительный процесс (10-20 минут), и страница в браузере зависает.

Мне нужно следующее: если пользователь нажимает кнопку generateReport1, он должен иметь возможность нажать и другую кнопку generateReport2, и будут работать два процесса.

Как я могу это сделать? Я думаю, мне нужно генерировать каждый процесс в новом потоке, но, может быть, в Spring Boot эта работа другая?

Комментарии:

1. Рассматривали ли вы асинхронные запросы? Веб-сокеты? Проблема в том, что ваши методы занимают слишком много времени. Таким образом, вместо этого вы должны отделить это с помощью компонента с отслеживанием состояния (с привязкой к сеансу). Затем это может быть опрошено / запрошено интерфейсом.

Ответ №1:

Мне нужно следующее: если пользователь нажимает кнопку generateReport1, он должен иметь возможность нажать и другую кнопку generateReport2, и будут работать два процесса.

Я бы использовал пул потоков для запуска генерации в фоновом режиме, что позволяет веб-странице сообщать «Обработка начата. Обновите эту страницу, чтобы проверить результаты «. Код для выполнения фоновой обработки может быть примерно следующим:

  private final ExecutorService threadPool = Executors.newFixedThreadPool(1);

 // you should make this class a DisposableBean
 public void destroy() {
     // shutdown the pool
     threadPool.shutdown();
 }

 // this is similar to generateReport2
 public void generateReport1() {
     // submit the generator to a background thread
     threadPool.submit(new Runnable() {
        public void run() {
            // do the actual generation in the thread
        }
     });
 }
  

Это заботится о фоновом потоке, но это не объясняет, как веб-пользователь (который запускается в другом потоке) увидит результаты, которые были созданы в фоновом режиме. Есть несколько способов, которыми вы можете это сделать.

  • Вы могли бы отправить им по электронной почте ссылку на сгенерированный PDF-файл, который сохраняется в файловой системе веб-сервера или что-то в этом роде.
  • Вы могли бы поместить a AtomicReference<PdfHolder> в сеанс пользователя, а затем сгенерировать запись в атомарную ссылку, когда это будет сделано. Затем веб-страница будет искать AtomicReference значение, отличное от null.
  • Вы могли бы поместить некоторый UUID в сеанс пользователя и передать тот же UUID генерирующему потоку, который запишет файл на диск под этим именем. Затем веб-страница будет искать существующий файл.