请参见官方文档Spring指南之创建异步方法
请参见Spring Framework官方文档之Task Execution and Scheduling(任务执行与调度)
参见github代码
文章目录
一.简介
本指南将引导你创建到GitHub的异步查询。重点是异步部分,这是scaling services时经常使用的一个特性。
二.你将创造什么(What You Will Build)
你将构建一个通过GitHub的API查询GitHub用户信息和检索数据的查找服务。scaling services 的一种方法是在后台运行开销很大的作业,然后使用Java的CompletableFuture接口等待结果。Java的CompletableFuture是从常规Future演变而来的。它可以很容易地将多个异步操作串联起来,并将它们合并到单个异步计算中。
三.创建项目
创建spring boot项目
单击Dependencies并选择Spring Web。
3.1 创建github用户实体类
public class GitHubUser {
//登陆账号
private String login;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
@Override
public String toString() {
return "GitHubUser{" +
"login='" + login + '\'' +
'}';
}
}
3.1 创建 GitHub 查找服务
@Service
public class GitHubLookupService {
private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);
//使用Spring的RestTemplate来调用一个远程REST点(api.github.com/users/),然后将答案转换为GitHubUser对象。
private final RestTemplate restTemplate;
//Spring Boot自动提供了一个RestTemplateBuilder,它可以用任何自动配置位(即MessageConverter)自定义默认值。
public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
/**
* findUser方法用Spring的@Async注释标记,表明它应该在一个单独的线程上运行。该方法的返回类型是CompletableFuture,而不是GitHubUser,这是任何异步服务的要求。
* 这段代码使用completedFuture方法返回一个CompletableFuture实例,它已经完成了GitHub查询的结果。
*/
@Async
public CompletableFuture<GitHubUser> findUser(String loginAccount) throws InterruptedException {
logger.info("Looking up " + loginAccount + "comes from Thread " + Thread.currentThread().getName());
String url = String.format("https://api.github.com/users/%s", loginAccount);
GitHubUser results = restTemplate.getForObject(url, GitHubUser.class);
// Artificial delay of 1s for demonstration purposes(为演示目的,人工延迟5秒)
Thread.sleep(5000L);
return CompletableFuture.completedFuture(results);
}
}
3.2 创建 GitHub 查找控制器
@Controller
@RequestMapping("/github")
public class GitHubLookupController {
private static final Logger logger = LoggerFactory.getLogger(GitHubLookupController.class);
private final GitHubLookupService gitHubLookupService;
public GitHubLookupController(GitHubLookupService gitHubLookupService) {
this.gitHubLookupService = gitHubLookupService;
}
@RequestMapping("/findUser")
@ResponseBody
public List<GitHubUser> findUser() throws InterruptedException, ExecutionException {
//开始计时
long start = System.currentTimeMillis();
//触发多个异步查找
CompletableFuture<GitHubUser> future1 = gitHubLookupService.findUser("PivotalSoftware");
CompletableFuture<GitHubUser> future2 = gitHubLookupService.findUser("CloudFoundry");
CompletableFuture<GitHubUser> future3 = gitHubLookupService.findUser("Spring-Projects");
CompletableFuture<GitHubUser> future4 = gitHubLookupService.findUser("xxxxxxxx");
//等待它们全部完成
CompletableFuture.allOf(future1, future2, future3, future4).join();
//输出结果,包括运行时间
logger.info("Elapsed time:" + (System.currentTimeMillis() - start));
logger.info("--> " + future1.get());
logger.info("--> " + future2.get());
logger.info("--> " + future3.get());
logger.info("--> " + future4.get());
return Arrays.asList(future1.get(), future2.get(), future3.get(), future4.get());
}
}
四.postman测试
1.请注意,前两个调用发生在不同的线程中(GithubLookup-2、GithubLookup-1),第三、四个调用被暂停,直到其中一个线程可用。要比较不使用异步功能需要多长时间,请尝试去掉@Async注释并再次运行该服务。总运行时间应该显著增加,因为每个查询至少需要五秒钟。例如,还可以调整Executor以增加corePoolSize属性。本质上,任务花费的时间越长,同时调用的任务越多,实现异步化的好处就越多。取舍是处理CompletableFuture接口。它增加了一层间接性,因为您不再直接处理结果。