Spring Bootを使用して小さなRESTを作成しようとしています。私はSpringを使用したことがなく、Javaをずっと前に使用しました(Java 7)!
過去2年間、私はPythonとC#のみを使用しました(しかし、私が言ったように、すでにJavaを使用しました)。
だから、今、私は非同期メソッドを使用してRESTを作成しようとしており、いくつかの例を確認しましたが、それでもこれを行うための「正しい方法」をよく理解していません。
次のドキュメントを参照してください: http://carlmartensen.com/completablefuture-deferredresult-async 、Java 8にはSpringで使用できるCompletableFuture
があります。 、次のコードを作成しました:
サービス:
@Service
public class UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Async
public CompletableFuture<User> findByEmail(String email) throws InterrupedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
リポジトリ:
public interface UserRepository extends MongoRepository<User, String> {
@Async
findByEmail(String email);
}
RestController:
@RestController
public class TestController {
private UserService userService;
public TestController(UserService userService) {
this.userService = userService;
}
@RequestMapping(value = "test")
public @ResponseBody CompletableFuture<User> test(@RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
}
}
このコードにより、期待される出力が得られます。次に、別のドキュメントを見ると(申し訳ありませんが、リンクを失いました)、Springが次のコードを受け入れていることがわかります(期待される出力も得られます)。
@RequestMapping(value = "test")
public @ResponseBody CompletableFuture<User> test(@RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email);
}
}
2つの方法に違いはありますか?
次に、次のガイドを参照してください: https://spring.io/guides/gs/async-method/ 、SpringBootApplication
クラスに@EnableAsync
注釈があります。 @EnableAsync
アノテーションを含めて、最後のリンクのコードのようなasyncExecutor
Beanを作成した場合、アプリケーションは/test
エンドポイントで何も返しません(200 OK応答のみで、本文は空白です)。
だから、私の残りは@EnableAsync
アノテーションなしで非同期ですか?そして、なぜ@EnableAsync
を使用すると、応答の本文が空白になるのですか?
UserRepositoryクラスのfindEmailメソッドで@Async
注釈が使用されているため、応答本文は空白です。これは、findByEmailメソッドが他の異なるスレッドで実行されているため、次の文User user = userRepository.findByEmail(email);
Listオブジェクトの代わりにnullを返します。
@Async
アノテーションは、@EnableAsync
を宣言したときに有効になります。これは、@EnableAsync
を使用した場合にのみ発生する理由です。findEmailメソッドの@Asyncをアクティブにして他のスレッドで実行するためです。
メソッドreturn userService.findByEmail(email);
は、CompletableFuture
クラスから作成されたUserService
オブジェクトを返します。
2番目のメソッド呼び出しとの違いは、thenApplyAsync
メソッドはuserService.findByEmail(email)
からの以前のメソッドからまったく新しいCompletableFuture
を作成し、からのユーザーオブジェクトのみを返すことです。最初のCompletableFuture
。
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
期待される結果を取得する場合は、findByEmailメソッドから@Async
注釈を削除し、最後に@EnableAsync
注釈を追加します
非同期メソッドの使用方法のアイデアを明確にする必要がある場合、3つのメソッドを呼び出す必要があり、各メソッドが完了するまで2秒かかるとしましょう。通常のシナリオでは、それらをmethod1、method2、最後にmethod3の順に呼び出します。リクエスト全体に6秒かかります。非同期アプローチを有効にすると、そのうち3つを呼び出して、6秒ではなく2秒待つことができます。
この長いメソッドをユーザーサービスに追加します。
@Async
public CompletableFuture<Boolean> veryLongMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(true);
}
そして、このようにControllerから3回呼び出します
@RequestMapping(value = "test")
public @ResponseBody CompletableFuture<User> test(@RequestParam(value = "email", required = true) String email) throws InterruptedException {
CompletableFuture<Boolean> boolean1= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean2= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean3= siteService.veryLongMethod();
CompletableFuture.allOf(boolean1,boolean2,boolean3).join();
return userService.findByEmail(email);
}
最後に、応答にかかる時間を測定します。6秒以上かかる場合はAsyncメソッドを実行していません。2秒しかかからない場合は成功します。
次のドキュメントも参照してください: @ Async Annotation 、 Spring async methods 、 CompletableFuture class
お役に立てば幸いです。
非同期メソッドをトリガーするときにパフォーマンスの問題に直面しています。非同期の子スレッドは非常に遅く実行を開始します(約20〜30秒の遅延)。私はメインのSpringBootアプリケーションクラスでThreadPoolTaskExecutor()を使用しています。パフォーマンスを要因と考える場合も、同じことを試すことができます。