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は非推奨となっているようです。
https://spring.pleiades.io/spring/docs/current/javadoc-api/deprecated-list.html
@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<String> async(String seconds) throws InterruptedException { | |
Thread.sleep(Long.valueOf(seconds) * 1000L); | |
return CompletableFuture.completedFuture(seconds + "秒かかる処理"); | |
} | |
} |
以下実行例です。
1 2 3 4 5 6 |
curl -X GET http://localhost:8080/sleep/2/3/1 --> 2秒かかる処理 --> 3秒かかる処理 --> 1秒かかる処理 処理時間:3018 ms |
@Asyncアノテーションをコメントアウトした場合の実行例です。同期処理されるため、実行時間がかかっています。
1 2 3 4 5 6 |
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の大体のリソースです
コメントはやさしくお願いいたします^^
座右の銘は、「狭き門より入れ」「願わくは、我に七難八苦を与えたまえ」です^^