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/
KHI入社して退社。今はCONFRAGEで正社員です。関西で140-170/80~120万から受け付けております^^
得意技はJS(ES6),Java,AWSの大体のリソースです
コメントはやさしくお願いいたします^^
座右の銘は、「狭き門より入れ」「願わくは、我に七難八苦を与えたまえ」です^^
コメント