昨天線上的導出文件數據出現了問題(導出的數據= A表通過sql查出來+B表通過異步接口查詢出來數據拼裝),經過排查是由於我使用了Spring core包中的ThreadPoolTaskExecutor類去異步獲取另一個B表中的數據,B表中的數據還沒有完全返回回來。接口就已經返回了,所以導致導出數據問題。
1. 導出數據會丟失的代碼如下:
<!-- 通用異步執行器 -->
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="30" />
</bean>
@Autowired
TaskExecutor taskExecutor;
List<OrderDto> orderDtoList = new ArrayList<>();
List<Order> orderList = OrderService.selectOrderList(query);
for (Order brokerOrder : orderList) {
OrderDto orderDto = new OrderDto();
BeanUtils.copyProperties(brokerOrder, orderDto);
taskExecutor.execute(new Runnable() {
@Override
public void run() {
try {
// 查計劃信息
AmountQuery amountQuery = new AmountQuery();
amountQuery.setOrderId(brokerOrder.getOrderId());
amountQuery.setOrderCode(brokerOrder.getOrderCode());
Return<Result<AmountDto>> amountList = amountFacadeService.selectOrderPayAmountList(amountQuery);
Result pageResult = amountList.R;
if (pageResult != null && pageResult.getData() != null && pageResult.getData().size() > 0) {
List<AmountDto> alist = new ArrayList<>();
List<AmountDto> list = pageResult.getData();
for (AmountDto dto : list) {
alist.add(dto);
}
orderDto.setAmountList(alist);
}
} catch (Exception e) {
LOG.info("error ",e);
}
}
});
orderDtoList.add(orderDto);
}
int count = OrderService.selectOrderListCount(query);
return Return.create(new Result<OrderDto>(count, orderDtoList));
2. 然後換成了多線程的Futrue後,問題解決,部分代碼如下:
private final ExecutorService es = new ThreadPoolExecutor(8, 10, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(5000));
List<TruckBrokerOrderDto> orderDtoList = new ArrayList<>();
List<order> orderList = orderService.selectOrderList(query);
Map<String, Future<orderDto>> futureMap = new HashMap<>();
for (Order order : orderList) {
Future<OrderDto> orderFuture = es.submit(new Callable<OrderDto>() {
@Override
public OrderDto call() {
OrderDto orderAmountDto = new OrderDto();
// 查計劃信息
AmountQuery amountQuery = new AmountQuery();
amountQuery.setOrderId(order.getOrderId());
amountQuery.setOrderCode(order.getOrderCode());
List<Amount> amountList = amountService.selectAmountList(amountQuery);
if (amountList != null && amountList.size() > 0) {
// 數據處理
}
return orderAmountDto;
}
});
futureMap.put(order.getOrderCode(), orderFuture);
}
for (Order order : orderList) {
OrderDto orderDto = new OrderDto();
BeanUtils.copyProperties(brokerOrder, orderDto);
try {
OrderDto orderAmountDto = futureMap.get(order.getOrderCode()).get();
if (orderAmountDto.getAmountList() != null && orderAmountDto.getAmountList().size() > 0) {
orderDto.setAmountList(orderAmountDto.getAmountList());
}
} catch (Exception e) {
LOG.info("error " ,e);
}
orderDtoList.add(orderDto);
}
int count = OrderService.selectOrderListCount(query);
returnReturn.create(new Result<OrderDto>(count, orderDtoList));
總結:
一般不需要對異步返回數據做處理的話,可以直接用Spring core包中的ThreadPoolTaskExecutor類直接執行(會丟失數據),比如發送短信,異步更新數據不需要返回結果等。
假如要對異步返回數據做處理的話,請用多線程的Futrue模式,可以保證數據不丟失。