1 背景
最近的项目中,有这样的场景:
根据商品id列表,分别调用不同服务查询这批商品的价格、库存、卖点等信息,然后将这批商品信息组合返回。
都是根据商品id查询的不同服务,如果拿着id挨个服务去串行执行(先查询价格服务->再查询库存服务->再查询卖点服务...),由于这些服务都是远程调用,很明显性能很低,而且后面的服务查询不依赖前面的查询结果,明显没有串行执行的必要。
2 场景模拟
2.1 服务准备
简化的商品VO:
@Data
public class ItemVO {
private Long itemId;
private Long price;
private Long quantity;
private List sellings;
}
模拟价格查询器:
public class PriceQueryService {
public Map queryPrice(List itemIds) {
Map result = new HashMap<>(itemIds.size());
ThreadLocalRandom random = ThreadLocalRandom.current();
itemIds.forEach(id -> {
Long price = random.nextLong(30000);
result.put(id, price);
});
// 模拟延迟
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
模拟库存查询器:
public class QuantityQueryService {
public Map queryQuantity(List itemIds) {
Map result = new HashMap<>(itemIds.size());
ThreadLocalRandom random = ThreadLocalRandom.current();
itemIds.forEach(id -> {
Long price = random.nextLong(100);
result.put(id, price);
});
// 模拟延迟
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
模拟卖点信息查询器:
public class SellingsQueryService {
public Map> querySellingText(List itemIds) {
Map> result = new HashMap<>(itemIds.size());
itemIds.forEach(id -> {
List text = Arrays.asList("可拆装","刺绣");
result.put(id, text);
});
// 模拟延迟
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
2.2 CompletableFuture调用各个服务
串行执行,1264毫秒左右
public class QueryItemInfoService {
private static final Logger log = LoggerFactory.getLogger(QueryItemInfoService.class);
public static void main(String[] args) {
log.info("start");
Long start = System.currentTimeMillis();
List itemIds = Arrays.asList(31332L, 23123L, 43834L, 34442L, 23213L, 434112L, 45342L);
PriceQueryService priceQueryService = new PriceQueryService();
QuantityQueryService quantityQueryService = new QuantityQueryService();
SellingsQueryService sellingsQueryService = new SellingsQueryService();
Map priceMap = priceQueryService.queryPrice(itemIds);
Map quantityMap = quantityQueryService.queryQuantity(itemIds);
Map> sellingsMap = sellingsQueryService.querySellingText(itemIds);
List itemVOS = itemIds.stream().map(
itemId -> {
ItemVO itemVO = new ItemVO();
itemVO.setItemId(itemId);
itemVO.setPrice(priceMap.getOrDefault(itemId, 0L));
itemVO.setQuantity(quantityMap.getOrDefault(itemId, 0L));
itemVO.setSellings(sellingsMap.getOrDefault(itemId, Collections.emptyList()));
return itemVO;
}
).collect(Collectors.toList());
Long end = System.currentTimeMillis();
log.info("end, duration = {}", end - start);
}
}
异步执行, 550毫秒左右
public class QueryItemInfoService {
private static final Logger log = LoggerFactory.getLogger(QueryItemInfoService.class);
public static void main(String[] args) {
log.info("start");
Long start = System.currentTimeMillis();
List itemIds = Arrays.asList(31332L, 23123L, 43834L, 34442L, 23213L, 434112L, 45342L);
PriceQueryService priceQueryService = new PriceQueryService();
QuantityQueryService quantityQueryService = new QuantityQueryService();
SellingsQueryService sellingsQueryService = new SellingsQueryService();
CompletableFuture
3 深入分析
CompletableFuture 是Java8引入的,实现了Future和CompletionStage两个接口。弥补了Future类无法手动完成,无法链式调用等问题。
常用的两个方法是runAsync和supplyAsync,两个方法的区别是,runAsync的入参是实现了Runnable接口的方法,也就是说没有返回值,返回的是CompletableFuture