详解Java8 CompletableFuture的并行处理用法

网友投稿 403 2022-08-01


目录前言场景用法1、在线API2、编写在线API查询3、编写查询服务4、编写测试接口5、效果6、CompletableFuture并行查询7、编写测试接口8、CompletableFuture效果思考示例代码

前言

工作中你可能会遇到很多这样的场景,一个接口,要从其他几个service调用查询方法,分别获取到需要的值之后再封装数据返回。

还可能在微服务中遇到类似的情况,某个服务的接口,要使用好几次feign去调用其他服务的方法获取数据,最后拿到想要的值并封装返回给前端。

这样的场景下,当某个或多个rpc调用的方法比较耗时,整个接口的响应就会非常慢。java8之后,有一个工具非常适合处理这种场景,就是CompletableFuture。

场景

本章主要讲解CompletableFuture的并行处理用法,来针对这种很常见的场景,帮助大家快速掌握并应用到实际工作当中。CompletableFuture内部的用法还有许多,但个人用到的场景大多都是并行处理,对其他场景感兴趣的小伙伴可以另行百度搜索。

场景说明:

写一个接口,调用另外两个HTTP接口,分别获取二十四节气和星座,最后放在一起返回。

用法

1、在线API

我们访问极速数据网站,注册一个账号,就可以免费使用里面的一些在线API,平均每天有100次免费机会,对于我这样经常本地做一些测试的人来说完全够用了。

这里,我使用了其中的查询二十四节气API,和查询星座API,后面会提供案例代码,也可以直接使用我的。

2、编写在线API查询

这里,我们在查询时,模拟耗时的情况。

1.查询二十四节气

package com.example.async.service;

import cn.hutool.http.HttpUtil;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

/**

*

* 查询二十四节气的服务

*

*

* @since 2022-04-26 15:25

*/

@Service

@Slf4j

public class TwentyFourService {

public static final String APPKEY = "xxxxxx";// 你的appkey

public static final String URL = "https://api.jisuapi.com/jieqi/query";

public String getResult() {

String url = URL + "?appkey=" + APPKEY;

String result = HttpUtil.get(url);

GjUmvOtvM // 模拟耗时

try {

TimeUnit.SECONDS.sleep(5);

} catch (Exception e) {

log.error("[二十四节气]>>>> 异常: {}", e.getMessage(), e);

}

return result;

}

}

2.查询星座

package com.example.async.service;

import cn.hutool.http.HttpUtil;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**

*

* 查询星座的服务

*

*

* @since 2022-04-26 15:25

*/

@Service

@Slf4j

public class ConstellationService {

public static final String APPKEY = "xxxxxx";// 你的appkey

public static final String URL = "https://api.jisuapi.com/astro/all";

public String getResult() {

String url = URL + "?appkey=" + APPKEY;

String result = HttpUtil.get(url);

// 模拟耗时

try {

TimeUnit.SECONDS.sleep(5);

} catch (Exception e) {

log.error("[星座]>>>> 异常: {}", e.getMessage(), e);

}

return result;

}

}

3、编写查询服务

package com.example.async.service;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import java.util.HashMap;

import java.util.Map;

/**

*

* 查询服务

*

*

* @since 2022-04-26 17:38

*/

@Service

@Slf4j

public class QueryService {

private final TwentyFourService twentyFourService;

private final ConstellationService constellationService;

public QueryService(TwentyFourService twentyFourService, ConstellationService constellationService) {

this.twentyFourService = twentyFourService;

this.constellationService = constellationService;

}

/**

* 同步返回结果

* @return 结果

*/

public Map query() {

// 1、查询二十四节气

String twentyFourResult = twentyFourService.getResult();

// 2、查询星座

String constellationResult = constellationService.getResult();

// 3、返回

Map map = new HashMap<>();

map.put("twentyFourResult", twentyFourResult);

map.put("constellationResult", constellationResult);

return map;

}

}

4、编写测试接口

这里,我们专门加上了耗时计算。

package com.example.async.controller;

import cn.hutool.core.date.TimeInterval;

import com.example.async.service.QueryService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**

*

* 测试

*

*

* @since 2022-04-26 17:35

*/

@RestController

@RequestMapping("/api")

@Slf4j

public class TestController {

private final QueryService queryService;

public TestController(QueryService queryService) {

this.queryService = queryService;

}

/**

* 同步查询

* @return 结果

*/

@GetMapping("/query")

public ResponseEntity> query() {

// 计时

final TimeInterval timer = new TimeInterval();

timer.start();

Map map = queryService.query();

map.put("costTime", timer.intervalMs() + " ms");

return ResponseEntity.ok().body(map);

}

}

5、效果

可以看到,两个接口一共耗费了10秒左右才返回。

6、CompletableFuture并行查询

现在我们来使用CompletableFuture改造下接口,并行查询两个HTTP接口再返回。

package com.example.async.service;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.CompletableFuture;

/**

*

* 查询服务

*

*

* @since 2022-04-26 17:38

*/

@Service

@Slf4j

public class QueryService {

private final TwentyFourService twentyFourService;

private final ConstellationService constellationService;

public QueryService(TwentyFourService twentyFourService, ConstellationService constellationService) {

this.twentyFourService = twentyFourService;

this.constellationService = constellationService;

}

/**

* 异步返回结果

* @return 结果

*/

public Map queryAsync() {

GjUmvOtvM Map map = new HashMap<>();

// 1、查询二十四节气

CompletableFuture twentyFourQuery = CompletableFuture.supplyAsync(twentyFourService::getResult);

twentyFourQuery.thenAccept((result) -> {

log.info("查询二十四节气结果:{}", result);

map.put("twentyFourResult", result);

}).exceptionally((e) -> {

log.error("查询二十四节气异常: {}", e.getMessage(), e);

map.put("twentyFourResult", "");

return null;

});

// 2、查询星座

CompletableFuture constellationQuery = CompletableFuture.supplyAsync(constellationService::getResult);

constellationQuery.thenAccept((result) -> {

log.info("查询星座结果:{}", result);

map.put("constellationResult", result);

}).exceptionally((e) -> {

log.error("查询星座异常: {}", e.getMessage(), e);

map.put("constellationResult", "");

return null;

});

// 3、allOf-两个查询必须都完成

CompletableFuture allQuery = CompletableFuture.allOf(twentyFourQuery, constellationQuery);

CompletableFuture> future = allQuery.thenApply((result) -> {

log.info("------------------ 全部查询都完成 ------------------ ");

return map;

}).exceptionally((e) -> {

log.error(e.getMessage(), e);

return null;

});

// 获取异步方法返回值

// get()-内部抛出了异常需手动处理; join()-内部处理了异常无需手动处理,点进去一看便知。

future.join();

return map;

}

}

7、编写测试接口

package com.example.async.controller;

import cn.hutool.core.date.TimeInterval;

import com.example.async.service.QueryService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**

*

* 测试

*

*

* @since 2022-04-26 17:35

*/

@RestController

@RequestMapping("/api")

@Slf4j

public class TestController {

private final QueryService queryService;

public TestController(QueryService queryService) {

this.queryService = queryService;

}

/**

* 异步查询

* @return 结果

*/

@GetMapping("/queryAsync")

public ResponseEntity&lGjUmvOtvMt;Map> queryAsynGjUmvOtvMc() {

// 计时

final TimeInterval timer = new TimeInterval();

timer.start();

Map map = queryService.queryAsync();

map.put("costTime", timer.intervalMs() + " ms");

return ResponseEntity.ok().body(map);

}

}

8、CompletableFuture效果

可以看到,时间缩短了一倍。

思考

如果在微服务中,有一个很复杂的业务需要远程调用5个第三方laji厂家的接口,每个接口假设都耗时5秒,使用CompletableFuture并行处理最终需要多久?

答案是肯定的,同步查询需要25秒左右,CompletableFuture并行处理还是5秒左右,也就是说,同一个接口中,调用的耗时接口越多,CompletableFuture优化的幅度就越大。

示例代码

可以下载我的完整示例代码本地按需测试,里面有我的极速数据API的key,省得自己注册账号了,每天免费就100次,先到先得哦。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java从源码看异步任务计算FutureTask
下一篇:springboot如何获取application.yml里值的方法(springboot读取yml文件内容)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~