目前我们需要对接第三方API,我们需要全量同步一些业务数据,于是编写了一套公共的处理逻辑,此处仅做记录。
/**
* 任务调度
* @param fetchFunction 拉取任务
* @param totalNumFunction 总数获取任务
* @param storeFunction 存储任务
* @return 任务是否成功
*/
public boolean startFetchingAndStoringOrders(Supplier<String> fetchFunction,
Function<String, Integer> totalNumFunction,
Consumer<String> storeFunction) {
final ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> fetchFuture = executor.submit(() -> {
try {
fetchOrders(fetchFunction, totalNumFunction);
} catch (Exception e) {
log.error("Error in fetchOrders", e);
stopSignal.set(true);
}
});
Future<?> storeFuture = executor.submit(() -> {
try {
storeOrders(storeFunction);
} catch (Exception e) {
log.error("Error in storeOrders", e);
stopSignal.set(true);
}
});
try {
// 等待任务完成
fetchFuture.get();
storeFuture.get();
} catch (InterruptedException | ExecutionException e) {
log.error("Error waiting for tasks to complete", e);
return false;
} finally {
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
List<Runnable> droppedTasks = executor.shutdownNow();
log.info("Executor did not terminate in the specified time.");
log.info("Dropped " + droppedTasks.size() + " tasks.");
}
} catch (InterruptedException ie) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
return goalSignal.get();
}
/**
* 公共拉取逻辑
*
* @param fetchFunction 不用的订单 不同的拉取方式
* @param totalNumFunction 不同的json,不同的总数处理方式
*/
private void fetchOrders(Supplier<String> fetchFunction,
Function<String, Integer> totalNumFunction) throws Exception {
try {
int pageNumber = 1;
int totalNum = 0;
do {
String json = fetchFunction.get(); // 获取数据
totalNum = totalNumFunction.apply(json); // 从 JSON 中获取总数
queue.put(json); // 将 JSON 字符串放入队列
pageNumber++;
} while (pageNumber * PAGE_SIZE < totalNum && !stopSignal.get());
} catch (Exception e) {
log.error("Error during fetching orders", e);
stopSignal.set(true);
goalSignal.set(false);
throw e; // 抛出异常
}
goalSignal.set(true);
}
/**
* 数据处理逻辑
* @param storeFunction 存储实现
* @throws Exception 异常
*/
private void storeOrders(Consumer<String> storeFunction) throws Exception {
while (true) { // 永久循环,直到显式退出
if (stopSignal.get()){
break;
}
if (queue.isEmpty() && goalSignal.get()) {
// 如果队列为空且生产者已完成,则退出循环
break;
}
try {
String jsonOrder = queue.poll(1, TimeUnit.SECONDS); // 尝试从队列中取出数据,设置超时避免永久阻塞
if (jsonOrder != null) {
storeFunction.accept(jsonOrder); // 如果有数据,则处理
}
} catch (Exception e) {
log.error("Error while taking from the queue", e);
stopSignal.set(true);
goalSignal.set(false);
throw e; // 抛出异常
}
}
}
startFetchingAndStoringOrders 方法:
使用两个异步任务:一个用于获取数据(fetchOrders),另一个用于存储数据(storeOrders)。
使用 Future 来跟踪这两个任务的执行状态。
如果任一任务遇到异常,通过 stopSignal 通知另一个任务停止执行。
最后返回 goalSignal.get(),表明任务是否成功完成。
fetchOrders 方法:
循环获取数据,每次迭代调用 fetchFunction 获取 JSON 数据,并使用 totalNumFunction 从中提取总数信息。
将获取的数据放入 queue。
如果遇到异常,设置 stopSignal 和 goalSignal,并抛出异常。
storeOrders 方法:
不断从 queue 中取数据并处理。如果 queue 为空,等待最多1秒。
如果检测到 stopSignal 或 goalSignal 且队列为空,则终止循环。
如遇异常,设置 stopSignal 和 goalSignal,并抛出异常。
生产者-消费者模型:优化的多线程处理,实现数据的高效拉取和存储。
动态数据处理:支持不同的数据源和存储策略(自行实现),增加灵活性。
异常处理:增强的错误监控,确保流程的稳健性和可靠性。
同步机制:通过 AtomicBoolean 实现跨任务的同步控制,优雅地处理任务终止情况。
精确队列管理:使用 BlockingQueue 管理数据流,避免了生产者和消费者之间的潜在阻塞问题。
自适应任务结束:合理地处理队列空状态,确保所有数据被处理后任务能够自动结束。
优化等待策略,减少无效的等待和性能损失。
记录一次异步数据处理公共逻辑