Spring Bootでparallelや@EnableAsyncで非同期処理や並列処理を実装する

Spring Bootでparallelや@EnableAsyncで非同期処理や並列処理を実装する

Stream APIで並列処理を実装する

Java8以上の場合、stream apiでparallelメソッドで並列実行することが出来ます。

parallelメソッドは並列処理ですので非同期処理とは異なります。

以下ソースは、指定秒数かかるAPIを2回実行していますが、並列実行しているので指定秒数強で実行ができます。

/**
 * 指定秒数かかるAPI
 *
 * @param seconds
 * @return
 * @throws InterruptedException
 */
@RequestMapping(path = "/sleep/{seconds}", method = RequestMethod.GET)
public ResponseEntity<String> sleep(@PathVariable String seconds) throws InterruptedException {
  Thread.sleep(Long.valueOf(seconds) * 1000L); // スリープするだけ
  return ResponseEntity.ok("test");
}

/**
 * parallelで並列実行
 *
 * @param seconds
 * @return
 */
@RequestMapping(path = "/test/{seconds}", method = RequestMethod.GET)
public ResponseEntity<String> test(@PathVariable String seconds) {
  long startTime = System.currentTimeMillis();
  RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
  RestTemplate restTemplate = restTemplateBuilder.build();
  String url = String.format("http://localhost:8080/sleep/%s", seconds);
  List<String> list = List.of(url, url); // 並列
  String result =
    list.stream()
    .parallel()
    .map(
      e –> {
      return restTemplate.getForObject(url, String.class);
    })
    .collect(Collectors.joining(",", "{", "}"));
  long endTime = System.currentTimeMillis();
  System.out.println("処理時間:" + (endTime – startTime) + " ms");
  return ResponseEntity.ok(result);
}

Spring5ではAsyncRestTemplateは非推奨となっているようです。

301 Moved Permanently

@EnableAsync,@Asyncアノテーションを使用して非同期処理を実装する

Spring Bootで非同期処理を実装するには@EnableAsyncアノテーションを使用します。

起動クラスに@EnableAsyncアノテーションを付与します。これで@Asyncアノテーションを付与したメソッドが非同期で実行されるようになります。

package jp.co.confrage;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class AsyncControllerApplication {

  public static void main(String[] args) {
    SpringApplication.run(AsyncControllerApplication.class, args);
  }
}

コントローラから非同期処理をするasyncメソッドを3回呼び出しています。指定秒数かかるメソッドです。

package jp.co.confrage.presentation.controller;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import jp.co.confrage.presentation.service.DemoService;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class DemoController {

  private final DemoService demoService;

  /**
   * 指定秒数かかるAPI
   *
   * @param seconds1
   * @param seconds2
   * @param seconds3
   * @return
   * @throws InterruptedException
   * @throws ExecutionException
   */
  @RequestMapping(path = "/sleep/{seconds1}/{seconds2}/{seconds3}", method = RequestMethod.GET)
  public ResponseEntity<String> sleep(
      @PathVariable String seconds1, @PathVariable String seconds2, @PathVariable String seconds3)
      throws InterruptedException, ExecutionException {
    long startTime = System.currentTimeMillis();
    CompletableFuture<String> page1 = demoService.async(seconds1);
    CompletableFuture<String> page2 = demoService.async(seconds2);
    CompletableFuture<String> page3 = demoService.async(seconds3);

    CompletableFuture.allOf(page1, page2, page3).join(); // 終了まで待機する

    System.out.println("--> " + page1.get());
    System.out.println("--> " + page2.get());
    System.out.println("--> " + page3.get());
    long endTime = System.currentTimeMillis();
    System.out.println("処理時間:" + (endTime - startTime) + " ms");
    return ResponseEntity.ok("test");
  }
}

@Asyncアノテーションが付与されたメソッドは別スレッドで実行されます。以下サービスクラスです。

package jp.co.confrage.presentation.service;

import java.util.concurrent.CompletableFuture;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class DemoService {

  /**
   * 非同期処理
   *
   * @param seconds
   * @return
   * @throws InterruptedException
   */
  @Async
  public CompletableFuture async(String seconds) throws InterruptedException {
    Thread.sleep(Long.valueOf(seconds) * 1000L);
    return CompletableFuture.completedFuture(seconds + "秒かかる処理");
  }
}

以下実行例です。

curl -X GET http://localhost:8080/sleep/2/3/1

--> 2秒かかる処理
--> 3秒かかる処理
--> 1秒かかる処理
処理時間:3018 ms

@Asyncアノテーションをコメントアウトした場合の実行例です。同期処理されるため、実行時間がかかっています。

curl -X GET http://localhost:8080/sleep/2/3/1

--> 2秒かかる処理
--> 3秒かかる処理
--> 1秒かかる処理
処理時間:6010 ms

以下サイトによるとorg.springframework.core.task.TaskExecutorクラスで色々調整が可能です。

また、実行可能jarでもwarでも実装可能です。

https://spring.pleiades.io/guides/gs/async-method/

サンプルコード

コメント

株式会社CONFRAGE ITソリューション事業部をもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む

タイトルとURLをコピーしました