web-dev-qa-db-ja.com

Springで非同期を作成する方法REST?

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を使用すると、応答の本文が空白になるのですか?

14
Roberto Correia

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 AnnotationSpring async methodsCompletableFuture class

お役に立てば幸いです。

11
Daniel C.

非同期メソッドをトリガーするときにパフォーマンスの問題に直面しています。非同期の子スレッドは非常に遅く実行を開始します(約20〜30秒の遅延)。私はメインのSpringBootアプリケーションクラスでThreadPoolTask​​Executor()を使用しています。パフォーマンスを要因と考える場合も、同じことを試すことができます。

1
Kishor kumar R