Java 如何使用 Spring 进行异步 REST?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45473215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:02:43  来源:igfitidea点击:

How to make a async REST with Spring?

javaspringrestasynchronous

提问by Roberto Correia

I'm trying to make a small REST using Spring Boot. I've never used Spring and used Java a long time ago (Java 7)!

我正在尝试使用 Spring Boot 制作一个小型 REST。我很久以前从未使用过 Spring 并且使用过 Java(Java 7)!

In the last 2 years I have used only Python and C# (but like I said, I already used Java).

在过去的两年里,我只使用了 Python 和 C#(但就像我说的,我已经使用了 Java)。

So, now, I'm trying to make a REST using async methods, and checked several examples, but still, I don't understand very well the "correct way" to do this.

所以,现在,我正在尝试使用异步方法制作 REST,并检查了几个示例,但是,我仍然不太了解执行此操作的“正确方法”。

Looking at the following documentation: http://carlmartensen.com/completablefuture-deferredresult-async, Java 8 has CompletableFuturethat I can use with Spring, so, I made the following code:

查看以下文档:http: //carlmartensen.com/completablefuture-deferredresult-async,Java8CompletableFuture可以与 Spring 一起使用,因此,我编写了以下代码:

Service:

服务:

@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);
  }
}

Repository:

存储库

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;
    })
  }  
}

This code give me the expected output. Then, looking at another documentation (sorry, I lost the link), I see that Spring accept the following code (which give me the expected output too):

这段代码给了我预期的输出。然后,查看另一个文档(抱歉,我丢失了链接),我看到 Spring 接受以下代码(这也给了我预期的输出):

  @RequestMapping(value = "test")
  public @ResponseBody CompletableFuture<User> test(@RequestParam(value = "email", required = true) String email) throws InterruptedException {
    return userService.findByEmail(email);
  }  
}

Is there a difference between the two methods?

这两种方法有区别吗?

Then, looking at the following guide: https://spring.io/guides/gs/async-method/, there's a @EnableAsyncannotation in SpringBootApplicationclass. If I include the @EnableAsyncannotation and create a asyncExecutorBean like the code from last link, my application don't return nothing on /testendpoint (only a 200 OK response, but with blank body).

然后,查看以下指南:https://spring.io/guides/gs/async-method/,课堂上有一个@EnableAsync注释SpringBootApplication。如果我包含@EnableAsync注释并创建一个asyncExecutorBean 就像上一个链接中的代码一样,我的应用程序不会在/test端点上返回任何内容(只有 200 OK 响应,但正文为空白)。

So, my rest is async without the @EnableAsyncannotation? And why when I use @EnableAsync, the response body is blank?

所以,我的休息是没有@EnableAsync注释的异步?为什么当我使用时@EnableAsync,响应正文是空白的?

采纳答案by Daniel C.

The response body is blank because the @Asyncannotation is used at findEmail method of UserRepository class, it means that there is no data returned to the following sentence User user = userRepository.findByEmail(email);because findByEmail method is running on other different thread and will return null instead of a List object.

响应体是空白的,因为在@AsyncUserRepository 类的 findEmail 方法中使用了注解,这意味着没有数据返回到下面的句子,User user = userRepository.findByEmail(email);因为 findByEmail 方法运行在其他不同的线程上,将返回 null 而不是 List 对象。

The @Asyncannotation is enabled when you declare @EnableAsyncthat is the reason why it only happens when you use @EnableAsyncbecause it activates the @Async of findEmail method to run it on other thread.

@Async在声明注解被启用@EnableAsync,这就是为什么它,只有当你使用情况的原因@EnableAsync是因为它激活findEmail的@Async方法在其他线程中运行它。

The method return userService.findByEmail(email);will return a CompletableFutureobject that is created from UserServiceclass.

该方法return userService.findByEmail(email);将返回一个CompletableFutureUserService类创建的对象。

The difference with the second method call is that thenApplyAsyncmethod will create a totally new CompletableFuturefrom the previous one that comes from userService.findByEmail(email)and will only return the user object that comes from the first CompletableFuture.

与第二个方法调用的不同之处在于,该thenApplyAsync方法将创建一个CompletableFuture来自前一个的全新方法,userService.findByEmail(email)并且只会返回来自第一个的用户对象CompletableFuture

 return userService.findByEmail(email).thenApplyAsync(user -> {
      return user;
    })

If you want to get the expected results just remove the @Asyncannotation from findByEmail method, and finally add the @EnableAsyncAnnotation

如果你想得到预期的结果,只需@Async从 findByEmail 方法中删除annotation,最后添加@EnableAsyncAnnotation

If you need to clarify ideas of how to use Async methods, lets say that you have to call three methods and each one takes 2 seconds to finish, in a normal scenario you will call them method1, then method2 and finally method3 in that case you entire request will take 6 seconds. When you activate the Async approach then you can call three of them and just wait for 2 seconds instead of 6.

如果您需要阐明如何使用 Async 方法的想法,假设您必须调用三个方法,并且每个方法都需要 2 秒才能完成,在正常情况下,您将调用它们 method1,然后是 method2,最后是 method3,在这种情况下,您整个请求将需要 6 秒。当您激活 Async 方法时,您可以调用其中的三个,然后等待 2 秒而不是 6 秒。

Add this long method to user service:

将此长方法添加到用户服务中:

@Async
public  CompletableFuture<Boolean> veryLongMethod()  {

    try {
        Thread.sleep(2000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    return CompletableFuture.completedFuture(true);
}

And call it three times from Controller, like this

并从 Controller 调用它三次,就像这样

  @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);
  }  

Finally measure the time that takes your response, if it takes more than 6 seconds then you are not running Async method, if it takes only 2 seconds then you succeed.

最后测量您的响应时间,如果超过 6 秒,那么您没有运行 Async 方法,如果只需要 2 秒,那么您就成功了。

Also see the following documentation: @Async Annotation, Spring async methods, CompletableFuture class

另请参阅以下文档:@Async AnnotationSpring async methodsCompletableFuture 类

Hope it help.

希望有帮助。

回答by Kishor kumar R

I'm facing performance issues when triggering Async methods. The Asynchronous child threads start executing very late (around 20 to 30 seconds delay). I'm using ThreadPoolTaskExecutor() in my main SpringBoot application class. You can also try the same if you consider perfomance as a factor.

我在触发异步方法时面临性能问题。异步子线程开始执行很晚(大约延迟 20 到 30 秒)。我在我的主要 SpringBoot 应用程序类中使用 ThreadPoolTask​​Executor()。如果您将性能视为一个因素,您也可以尝试相同的方法。