本文目录导读:

在Java中融合多源数据(如数据库、API、文件、消息队列等),核心思路是统一入口、并行采集、标准化转换和聚合计算。
下面我通过一个电商订单综合报表的案例来详细拆解,展示如何将MySQL(业务库)、Redis(缓存)、第三方API(物流信息)、Excel(历史数据)融合为一份综合视图。
需求场景设定
目标:生成一张“订单综合看板”,包含订单基本信息(MySQL)、最新成交价(Redis)、实时物流状态(第三方API)、历史同比数据(Excel)。
核心架构设计(分层解耦)
graph TD
A[Controller层] --> B[Facade/Service层]
B --> C[DataFetcher层]
C --> C1[MySQL Fetcher]
C --> C2[Redis Fetcher]
C --> C3[APIClient]
C --> C4[ExcelReader]
C1 --> D[CompletableFuture/线程池]
C2 --> D
C3 --> D
C4 --> D
D --> E[数据聚合器/转换器]
E --> F[统一响应对象]
核心代码实现(关键片段)
(1) 定义统一数据模型(DTO)
// 综合订单DTO —— 融合了来自所有数据源的信息
@Data
@Builder
public class CompositeOrderDTO {
private String orderId; // 来自MySQL
private String userName; // 来自MySQL
private BigDecimal latestPrice; // 来自Redis(缓存了实时波动价)
// 物流信息(来自第三方API)
private String logisticsStatus;
private String logisticsCompany;
// 同比数据(来自Excel历史文件)
private BigDecimal lastYearAmount;
private BigDecimal growthRate;
// 融合时间
private LocalDateTime aggregatedAt;
}
(2) 定义各数据源的Fetcher(采集器)
public interface DataFetcher<T> {
T fetch(String key);
}
// 1. MySQL查询器
@Component("mysqlFetcher")
public class OrderMySQLFetcher implements DataFetcher<OrderEntity> {
@Override
public OrderEntity fetch(String orderId) {
// 实际走MyBatis/JPA
return orderMapper.selectById(orderId);
}
}
// 2. Redis查询器
@Component("redisFetcher")
public class PriceRedisFetcher implements DataFetcher<BigDecimal> {
@Override
public BigDecimal fetch(String orderId) {
String key = "order:price:" + orderId;
return new BigDecimal(redisTemplate.opsForValue().get(key));
}
}
// 3. 第三方API调用器
@Component("logisticsClient")
public class LogisticsAPIClient {
// 可以使用WebClient或RestTemplate
public LogisticsInfo fetchLogistics(String orderId) {
// 调用外部接口 https://api.logistics.com/query
}
}
// 4. Excel读取器(历史数据)
@Component("excelReader")
public class HistoricalExcelReader {
public HistoricalData fetchHistory(String orderId) {
// 使用Apache POI / EasyExcel读取本地文件
}
}
核心融合逻辑(并行采集 + 聚合)
@Service
public class OrderCompositeService {
// 注入服务
@Autowired
private OrderMySQLFetcher mysqlFetcher;
@Autowired
private PriceRedisFetcher redisFetcher;
@Autowired
private LogisticsAPIClient logisticsClient;
@Autowired
private HistoricalExcelReader excelReader;
/**
* 核心融合方法 —— 使用CompletableFuture并行采集
*/
public CompositeOrderDTO getCompositeOrder(String orderId) {
// 创建线程池(防止IO阻塞主线程)
ExecutorService executor = Executors.newFixedThreadPool(4);
try {
// 1. 并行发起4个独立的采集任务
CompletableFuture<OrderEntity> dbFuture =
CompletableFuture.supplyAsync(() -> mysqlFetcher.fetch(orderId), executor);
CompletableFuture<BigDecimal> priceFuture =
CompletableFuture.supplyAsync(() -> redisFetcher.fetch(orderId), executor);
CompletableFuture<LogisticsInfo> logisticFuture =
CompletableFuture.supplyAsync(() -> logisticsClient.fetchLogistics(orderId), executor);
CompletableFuture<HistoricalData> historyFuture =
CompletableFuture.supplyAsync(() -> excelReader.fetchHistory(orderId), executor);
// 2. 等待所有数据返回(类似Promise.all)
CompletableFuture.allOf(dbFuture, priceFuture, logisticFuture, historyFuture).join();
// 3. 获取各数据源的结果
OrderEntity order = dbFuture.get();
BigDecimal price = priceFuture.get();
LogisticsInfo logistics = logisticFuture.get();
HistoricalData history = historyFuture.get();
// 4. 数据转换 + 业务逻辑融合(计算增长率等)
BigDecimal growthRate = calculateGrowth(order.getAmount(), history);
// 5. 组装最终结果
return CompositeOrderDTO.builder()
.orderId(order.getOrderId())
.userName(order.getUserName())
.latestPrice(price)
.logisticsStatus(logistics.getStatus())
.logisticsCompany(logistics.getCompany())
.lastYearAmount(history.getAmount())
.growthRate(growthRate)
.aggregatedAt(LocalDateTime.now())
.build();
} catch (Exception e) {
// 异常处理:如果某个数据源挂掉,要有降级策略
return fallbackOnFailure(orderId, e);
} finally {
executor.shutdown(); // 关闭线程池
}
}
}
高级融合技巧与注意事项
(1) 超时与降级策略(避免吊死)
// 设置超时时间,防止第三方API过慢拖垮整个流程
CompletableFuture<LogisticsInfo> logisticFuture =
CompletableFuture.supplyAsync(() -> logisticsClient.fetchLogistics(orderId), executor)
.completeOnTimeout(new LogisticsInfo("UNKNOWN", "UNKNOWN"), 2, TimeUnit.SECONDS);
(2) 动态数据源路由(当数据源数量动态变化时)
// 使用策略模式管理多个数据源
Map<String, DataFetcher> fetcherMap = new HashMap<>();
fetcherMap.put("mysql", mysqlFetcher);
fetcherMap.put("redis", redisFetcher);
fetcherMap.put("api", new AdapterFetcher(apiClient));
// 根据配置动态决定采集哪些数据源(高级用户看物流,普通用户不看)
(3) 流式聚合(Stream API)(当需要融合列表时)
// 比如要融合不同系统发来的同一批订单的多个评分
List<DataSourceValue> sourceValues = getFromDifferentSources();
CompositeResult result = sourceValues.stream()
.map(v -> normalize(v)) // 标准化
.filter(Objects::nonNull) // 过滤空数据
.reduce(new CompositeResult(), CompositeResult::merge, CompositeResult::combine);
(4) 数据标准化与冲突解决(字段名不同,单位不同)
// 统一单位:将斤、KG、磅统一为克
private BigDecimal normalizeWeight(String source, BigDecimal value, String unit) {
if ("斤".equals(unit)) return value.multiply(new BigDecimal("500"));
if ("磅".equals(unit)) return value.multiply(new BigDecimal("453.6"));
return value; // 默认克
}
生产级框架推荐
如果数据源极多(几十个),手写兼容性差,建议用以下框架:
| 框架 | 适用场景 | 特点 |
|---|---|---|
| Spring Batch | 定时批量融合大量历史数据 | 分块读取、事务管理、失败重试 |
| Apache Camel | 系统间路由与企业集成模式 | 支持几十种协议(FTP、JMS等),以管道方式对接 |
| Flink/Spark Streaming | 实时数据流融合(如Kafka+Redis+数据库) | 时间窗口、状态管理、纬度关联 |
| Camunda | 复杂业务编排 | 可视化流程图驱动数据获取和决策 |
案例要点)
| 要点 | 实现方式 |
|---|---|
| 并行加速 | CompletableFuture + 自定义线程池 |
| 解耦 | 每个数据源独立成Component/Fetcher |
| 容错降级 | 超时控制 + fallback方法 |
| 聚合计算 | 在Service层完成业务计算(增长率等) |
| 统一出口 | 返回统一的Composite DTO隐藏底层差异 |
通过这种方式,你将IO密集型的数据源(数据库、API、文件)从串行等待(总耗时 = 4×单次耗时)优化为并行执行(总耗时 ≈ 最长单次耗时),且代码结构清晰,后续每加一个数据源,只需新增一个Fetcher并在聚合方法中调用即可。