📋 系统概述
核心功能
文档处理系统负责接收Kafka消息队列中的文件处理任务,自动完成文档解析、文本提取、向量化处理和搜索引擎存储,为AI智能问答提供知识基础。
技术架构
text
Kafka消息队列 → 消费者服务 → 文档解析 → 向量化处理 → Elasticsearch存储 (任务分发) (自动触发) (多格式解析) (语义理解) (向量检索)
🔄 核心处理流程
第一阶段:Kafka消费者自动触发
1. 消息监听机制
java
java
@KafkaListener(
topics = "#{kafkaConfig.getFileProcessingTopic()}", // 监听主题:file-processing-topic1
groupId = "#{kafkaConfig.getFileProcessingGroupId()}" // 消费者组:file-processing-group
)
public void processTask(FileProcessingTask task) {
// 有新消息时自动调用
}
2. 任务数据结构
json
java
{
"fileMd5": "abc123def456",
"objectUrl": "http://minio.example.com/uploads/merged/report.pdf?signature=...",
"fileName": "report.pdf",
"userId": "user123",
"orgTag": "default",
"isPublic": false
}
3. 自动处理流程
-
消息接收:Kafka Broker推送任务到消费者组
-
反序列化:JSON消息转换为FileProcessingTask对象
-
方法调用:自动执行processTask()方法
第二阶段:智能文档解析
1. 多源文件获取
java
java
// 支持多种文件来源
if (objectUrl.startsWith("http")) {
// HTTP下载方式
fileStream = httpClient.download(objectUrl);
} else if (objectUrl.startsWith("file:")) {
// 本地文件系统
fileStream = new FileInputStream(objectUrl);
} else {
// 类路径资源
fileStream = getClass().getResourceAsStream(objectUrl);
}
2. 流式处理优化
问题场景:
-
Tika解析器需要先读取文件头判断类型
-
然后重置到文件开头进行完整解析
-
普通InputStream不支持位置重置
解决方案:
java
java
// 转换为支持mark/reset的缓冲流
if (!inputStream.markSupported()) {
inputStream = new BufferedInputStream(inputStream);
}
inputStream.mark(1024 * 1024); // 标记位置,最多回退1MB
3. Apache Tika解析流程
java
java
// 创建流式内容处理器
StreamingContentHandler handler = new StreamingContentHandler(
text -> processTextChunk(text, fileMd5)
);
// 配置解析器
Metadata metadata = new Metadata();
metadata.set(Metadata.RESOURCE_NAME_KEY, fileName);
AutoDetectParser parser = new AutoDetectParser();
ParseContext context = new ParseContext();
// 执行解析
parser.parse(inputStream, handler, metadata, context);
4. 流式分块处理机制
核心优势:
-
内存高效:无需加载完整文件到内存
-
大文件支持:可处理GB级文档
-
实时处理:边解析边处理,延迟低
处理流程:
text
接收字符 → 缓冲累积 → 检查大小 → 处理分块 → 清空缓冲 ↓ ↓ ↓ ↓ ↓ Tika输出 → StringBuilder → 达到512字符 → 向量化+存储 → 准备接收
第三阶段:语义向量化处理
1. 向量化API调用
java
java
// 批量向量化配置
@Value("${embedding.batch-size:10}")
private int batchSize;
@Value("${embedding.api-url}")
private String apiUrl;
@Value("${embedding.model}")
private String modelName;
// API请求结构
{
"model": "text-embedding-v4",
"input": {
"texts": ["文本1", "文本2", ... "文本10"]
},
"parameters": {
"text_type": "document"
}
}
2. 向量生成结果
-
维度数量:2048维浮点向量
-
语义编码:将文本转换为高维空间中的点
-
相似度计算:向量距离反映语义相似度
-
示例向量 :
[0.123, -0.456, 0.789, ..., 0.234]
第四阶段:Elasticsearch存储
1. 文档结构设计
json
java
{
"_index": "knowledge_base",
"_source": {
"fileMd5": "abc123def456",
"fileName": "report.pdf",
"chunkIndex": 0,
"content": "这是文档的第一段内容...",
"vector": [0.123, -0.456, 0.789, ...], // 2048维
"userId": "user123",
"orgTag": "default",
"isPublic": false,
"createdAt": "2024-01-15T14:30:25.000Z",
"tokenCount": 128,
"language": "zh-CN"
}
}
2. 批量索引优化
java
java
// 使用Bulk API提高效率
BulkRequest bulkRequest = new BulkRequest();
for (DocumentChunk chunk : chunks) {
IndexRequest indexRequest = new IndexRequest("knowledge_base")
.source(chunk.toMap(), XContentType.JSON);
bulkRequest.add(indexRequest);
}
// 执行批量插入
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
⚙️ 关键技术特性
1. 异步处理架构
-
解耦设计:上传与处理分离,互不影响
-
弹性扩展:可根据负载动态调整消费者数量
-
失败重试:消息处理失败自动重试或进入死信队列
2. 流式处理优势
| 处理模式 | 内存占用 | 大文件支持 | 处理延迟 | 适用场景 |
|---|---|---|---|---|
| 传统加载 | 高(需完整加载) | 有限 | 高(等待加载) | 小文件 |
| 流式处理 | 低(按需加载) | 优秀 | 低(实时处理) | 大文件 |
3. 智能格式识别
-
支持格式:PDF、Word、Excel、PPT、TXT、HTML、Markdown等
-
自动检测:基于文件内容和扩展名双重判断
-
编码识别:自动检测文本编码(UTF-8、GBK等)
4. 内存安全机制
java
java
// 内存使用监控
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
if (heapUsage.getUsed() > heapUsage.getMax() * 0.8) {
// 内存使用超过80%,触发GC并调整处理策略
System.gc();
adjustProcessingStrategy();
}
📊 数据流转全视图
端到端处理链路
多存储协同工作
| 存储系统 | 数据类型 | 用途 | 生命周期 |
|---|---|---|---|
| MinIO | 原始文件 | 备份与下载 | 长期保留 |
| MySQL | 元数据/分块信息 | 管理与查询 | 长期保留 |
| Redis | 处理状态 | 进度跟踪 | 短期缓存 |
| Elasticsearch | 文本+向量 | 智能检索 | 长期检索 |
🔧 性能优化策略
1. 并行处理优化
java
java
// 支持并行处理的配置
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("documentProcessingExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("DocumentProcessor-");
executor.initialize();
return executor;
}
}
2. 批量操作策略
-
向量化批量:每批10个文本块,减少API调用
-
ES批量索引:Bulk API减少网络往返
-
数据库批量:JPA批量插入优化
3. 资源复用机制
-
连接池管理:数据库、Redis、ES连接复用
-
流式传输:避免内存中完整复制数据
-
缓存策略:常用解析器实例缓存
🚨 错误处理与容错
1. 分级错误处理策略
| 错误级别 | 处理方式 | 影响范围 | 恢复策略 |
|---|---|---|---|
| 轻微错误 | 记录日志,继续处理 | 单个分块 | 自动重试当前分块 |
| 中等错误 | 暂停处理,记录详细日志 | 单个文件 | 人工介入检查 |
| 严重错误 | 停止消费者,发送告警 | 整个服务 | 紧急维护,系统重启 |
2. 具体错误场景处理
场景1:文件下载失败
java
java
try {
InputStream stream = downloadFile(objectUrl);
} catch (IOException e) {
// 1. 检查URL有效性
if (isUrlExpired(objectUrl)) {
// 重新生成预签名URL
objectUrl = minioService.generateNewPresignedUrl(fileMd5);
retryDownload();
}
// 2. 网络问题重试
else if (e instanceof ConnectException) {
waitAndRetry(5, TimeUnit.SECONDS);
}
}
场景2:解析器异常
java
java
catch (TikaException e) {
// 1. 格式不支持:记录并跳过
if (e.getMessage().contains("Unsupported format")) {
log.warn("不支持的文件格式: {}", fileName);
return;
}
// 2. 文件损坏:标记为失败
else if (e.getMessage().contains("corrupted")) {
markFileAsCorrupted(fileMd5);
}
}
场景3:API调用失败
java
java
catch (EmbeddingApiException e) {
// 1. 配额超限:等待后重试
if (e.getErrorCode() == 429) {
exponentialBackoffRetry();
}
// 2. 认证失败:更新密钥
else if (e.getErrorCode() == 401) {
refreshApiKey();
}
}
📋 系统监控指标
1. 关键性能指标(KPI)
-
吞吐量:每分钟处理的文档数量
-
处理延迟:从接收到完成的平均时间
-
成功率:任务处理成功比例
-
资源使用:CPU、内存、磁盘IO
2. 业务监控指标
-
文档类型分布:各类格式处理数量
-
向量化质量:向量相似度分布
-
存储效率:ES索引大小与增长趋势
🎓 学习与调试指南
调试检查清单
理论理解(✅验证点)
-
Kafka消费者组工作机制
-
流式处理与传统处理的区别
-
为什么需要BufferedInputStream
-
向量化的数学原理
-
Elasticsearch向量检索机制
实践操作(✅验证点)
-
在processTask()方法设置断点
-
观察文档解析的字符流
-
查看向量API的请求响应
-
验证ES文档的存储结构
-
测试完整流程的端到端功能
代码理解(✅验证点)
-
FileProcessingConsumer类完整流程
-
ParseService的解析策略
-
VectorizationService的API调用
-
StreamingContentHandler的流处理逻辑
-
ElasticsearchService的存储实现
❓ 常见问题解答
Q1:为什么选择Kafka而不是直接同步处理?
A: 异步处理架构的优势:
-
用户体验:用户无需等待处理完成
-
系统解耦:上传服务与处理服务独立
-
弹性伸缩:可根据负载动态调整消费者数量
-
容错能力:消息持久化,失败可重试
Q2:流式处理解决了什么核心问题?
A: 解决大文件处理的三难问题:
-
内存瓶颈:无需将完整文件加载到内存
-
处理延迟:边解析边处理,实时性高
-
系统稳定性:避免大内存占用导致的OOM
Q3:为什么需要向量化处理?
A: 向量化是实现语义理解的关键:
-
语义编码:将文本转换为数学向量
-
相似度计算:向量距离反映语义距离
-
AI理解:为LLM提供可计算的语义表示
-
高效检索:支持基于语义的相似性搜索
Q4:Elasticsearch与其他存储方案对比?
A: ES在文档搜索场景的优势:
-
向量检索:原生支持kNN近似最近邻搜索
-
全文检索:成熟的分词和检索能力
-
实时性:近实时索引和搜索
-
扩展性:分布式架构支持水平扩展