278 lines
7.8 KiB
Markdown
278 lines
7.8 KiB
Markdown
# 完成图片保存重试机制
|
||
|
||
## 🔄 重试策略
|
||
|
||
为了确保4张完成图片都能成功保存,系统实现了**双层重试机制**。
|
||
|
||
## 📊 重试架构
|
||
|
||
### 第一层:网络层重试(ScriptClient)
|
||
|
||
**位置**: `ScriptClient.getImagePng()`
|
||
```java
|
||
.timeout(Duration.ofSeconds(10)) // 10秒超时
|
||
.retry(3) // 网络失败重试3次(立即重试)
|
||
```
|
||
|
||
**重试条件**:
|
||
- 网络连接失败
|
||
- HTTP错误
|
||
- 超时(10秒)
|
||
|
||
**重试间隔**: 立即重试(无延迟)
|
||
|
||
---
|
||
|
||
### 第二层:业务层重试(CompletionImageService)
|
||
|
||
**位置**: `CompletionImageService.downloadAndSaveImage()`
|
||
```java
|
||
.retryWhen(Retry.fixedDelay(3, Duration.ofMillis(500))
|
||
.doBeforeRetry(retrySignal -> {
|
||
long attempt = retrySignal.totalRetries() + 1;
|
||
log.warn("下载图片失败,开始第{}次重试: codeNo={}, imageName={}",
|
||
attempt, codeNo, imageName);
|
||
})
|
||
)
|
||
```
|
||
|
||
**重试条件**:
|
||
- 图片下载失败
|
||
- 文件写入失败
|
||
- 任何异常
|
||
|
||
**重试间隔**: 500毫秒固定延迟
|
||
|
||
**重试次数**: 3次
|
||
|
||
---
|
||
|
||
## 🎯 完整重试流程
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[开始下载图片] --> B[ScriptClient.getImagePng]
|
||
B --> C{成功?}
|
||
C -->|失败| D{第一层重试<3次?}
|
||
D -->|是| B
|
||
D -->|否| E[第一层失败]
|
||
|
||
C -->|成功| F[保存到文件系统]
|
||
E --> G{第二层重试<3次?}
|
||
F --> H{保存成功?}
|
||
|
||
H -->|失败| G
|
||
G -->|是| I[等待500ms]
|
||
I --> B
|
||
G -->|否| J[最终失败]
|
||
|
||
H -->|成功| K[保存成功]
|
||
```
|
||
|
||
## 📝 日志示例
|
||
|
||
### 成功场景(无重试)
|
||
```log
|
||
DEBUG - 开始下载图片: codeNo=MYNM5JHA, imageName=首次主页.png, scriptPath=/rr3/首次主页.png
|
||
DEBUG - 获取图片成功: path=/rr3/首次主页.png, 数据大小=245678字节
|
||
DEBUG - 图片保存成功: codeNo=MYNM5JHA, imageName=首次主页.png, size=245678字节, path=20251103/MYNM5JHA/homepage.png
|
||
```
|
||
|
||
### 重试场景(网络抖动)
|
||
```log
|
||
DEBUG - 开始下载图片: codeNo=MYNM5JHA, imageName=首次赏金.png, scriptPath=/rr3/首次赏金.png
|
||
WARN - 获取图片失败: path=/rr3/首次赏金.png, error=Connection timeout
|
||
WARN - 下载图片失败,开始第1次重试: codeNo=MYNM5JHA, imageName=首次赏金.png
|
||
DEBUG - 获取图片成功: path=/rr3/首次赏金.png, 数据大小=189234字节
|
||
DEBUG - 图片保存成功: codeNo=MYNM5JHA, imageName=首次赏金.png, size=189234字节
|
||
```
|
||
|
||
### 最终失败场景
|
||
```log
|
||
DEBUG - 开始下载图片: codeNo=MYNM5JHA, imageName=结束赏金.png, scriptPath=/rr3/结束赏金.png
|
||
WARN - 获取图片失败: path=/rr3/结束赏金.png, error=404 Not Found
|
||
WARN - 下载图片失败,开始第1次重试: codeNo=MYNM5JHA, imageName=结束赏金.png
|
||
WARN - 获取图片失败: path=/rr3/结束赏金.png, error=404 Not Found
|
||
WARN - 下载图片失败,开始第2次重试: codeNo=MYNM5JHA, imageName=结束赏金.png
|
||
WARN - 获取图片失败: path=/rr3/结束赏金.png, error=404 Not Found
|
||
WARN - 下载图片失败,开始第3次重试: codeNo=MYNM5JHA, imageName=结束赏金.png
|
||
WARN - 获取图片失败: path=/rr3/结束赏金.png, error=404 Not Found
|
||
ERROR - 下载图片失败,已重试3次: codeNo=MYNM5JHA, imageName=结束赏金.png, 最后错误=404 Not Found
|
||
WARN - 图片下载和保存最终失败: codeNo=MYNM5JHA, imageName=结束赏金.png, error=404 Not Found
|
||
INFO - 完成图片保存成功: codeNo=MYNM5JHA, 成功数量=3/4
|
||
```
|
||
|
||
## 📊 重试统计
|
||
|
||
### 单张图片最多重试次数
|
||
|
||
**总重试次数 = 第一层重试 × 第二层重试**
|
||
- 第一层(网络层):3次立即重试
|
||
- 第二层(业务层):3次延迟重试(每次500ms)
|
||
- **理论最大尝试**:1 + 3 + 3 = **7次**
|
||
- **实际有效重试**:约 **3-6次**(取决于失败类型)
|
||
|
||
### 超时时间
|
||
|
||
**单次尝试总超时**:
|
||
- 网络超时:10秒
|
||
- 网络重试:10s × 3 = 30秒
|
||
- **单次最长时间**:约40秒
|
||
|
||
**整体超时**:
|
||
- 业务层重试间隔:500ms × 3 = 1.5秒
|
||
- **最长总时间**:约120秒(极端情况)
|
||
|
||
## 🎯 容错处理
|
||
|
||
### 部分图片失败不影响整体
|
||
|
||
即使某张图片下载失败,其他图片依然会保存成功:
|
||
|
||
```json
|
||
{
|
||
"saveTime": "2025-11-03T20:30:45",
|
||
"codeNo": "MYNM5JHA",
|
||
"machineId": "rr3",
|
||
"dateFolder": "20251103",
|
||
"images": {
|
||
"homepage": "20251103/MYNM5JHA/homepage.png",
|
||
"first-reward": "20251103/MYNM5JHA/first-reward.png",
|
||
"mid-reward": "20251103/MYNM5JHA/mid-reward.png"
|
||
// "end-reward" 下载失败,未保存
|
||
},
|
||
"totalCount": 3 // 成功保存3张
|
||
}
|
||
```
|
||
|
||
### 数据库记录
|
||
|
||
**成功数量会记录在 completion_images 字段中**:
|
||
```sql
|
||
SELECT
|
||
code_no,
|
||
JSON_EXTRACT(completion_images, '$.totalCount') as saved_count,
|
||
completion_images_saved_at
|
||
FROM link_task
|
||
WHERE code_no = 'MYNM5JHA';
|
||
|
||
-- 结果:
|
||
-- code_no: MYNM5JHA
|
||
-- saved_count: 3
|
||
-- completion_images_saved_at: 2025-11-03 20:30:45
|
||
```
|
||
|
||
## 🔍 故障排查
|
||
|
||
### 检查某个任务的图片保存情况
|
||
|
||
```bash
|
||
# 查看日志
|
||
grep "codeNo=MYNM5JHA" logs/server.log | grep "图片"
|
||
|
||
# 查看文件系统
|
||
ls -lh completion-images/$(date +%Y%m%d)/MYNM5JHA/
|
||
|
||
# 查询数据库
|
||
mysql> SELECT code_no,
|
||
JSON_EXTRACT(completion_images, '$.totalCount') as count,
|
||
completion_images
|
||
FROM link_task
|
||
WHERE code_no = 'MYNM5JHA'\G
|
||
```
|
||
|
||
### 常见失败原因
|
||
|
||
| 错误类型 | 说明 | 重试是否有效 | 解决方案 |
|
||
|---------|------|--------------|----------|
|
||
| **Connection timeout** | 网络超时 | ✅ 有效 | 自动重试通常能解决 |
|
||
| **404 Not Found** | 图片不存在 | ❌ 无效 | 检查脚本端是否生成图片 |
|
||
| **500 Server Error** | 脚本端错误 | ⚠️ 部分有效 | 检查脚本端服务状态 |
|
||
| **Permission denied** | 文件权限错误 | ❌ 无效 | 检查目录权限 |
|
||
| **Disk full** | 磁盘空间不足 | ❌ 无效 | 清理磁盘空间 |
|
||
|
||
## 📈 优化建议
|
||
|
||
### 1. 监控重试率
|
||
|
||
```sql
|
||
-- 统计图片保存成功率
|
||
SELECT
|
||
DATE(completion_images_saved_at) as date,
|
||
COUNT(*) as total_tasks,
|
||
SUM(CASE
|
||
WHEN JSON_EXTRACT(completion_images, '$.totalCount') = 4
|
||
THEN 1 ELSE 0
|
||
END) as all_4_images,
|
||
SUM(CASE
|
||
WHEN JSON_EXTRACT(completion_images, '$.totalCount') < 4
|
||
THEN 1 ELSE 0
|
||
END) as partial_images,
|
||
ROUND(SUM(CASE
|
||
WHEN JSON_EXTRACT(completion_images, '$.totalCount') = 4
|
||
THEN 1 ELSE 0
|
||
END) * 100.0 / COUNT(*), 2) as success_rate
|
||
FROM link_task
|
||
WHERE completion_images_saved_at IS NOT NULL
|
||
GROUP BY DATE(completion_images_saved_at)
|
||
ORDER BY date DESC;
|
||
```
|
||
|
||
### 2. 告警阈值
|
||
|
||
建议设置告警:
|
||
- 如果成功率 < 80%,发送告警
|
||
- 如果某个图片重试率 > 50%,检查该图片源
|
||
|
||
### 3. 性能调优
|
||
|
||
```yaml
|
||
# application.yml - 可调整的参数
|
||
completion:
|
||
image:
|
||
retry:
|
||
max-attempts: 3 # 最大重试次数
|
||
delay-ms: 500 # 重试延迟(毫秒)
|
||
timeout-seconds: 10 # 单次下载超时
|
||
```
|
||
|
||
## ⚡ 性能影响
|
||
|
||
### 最佳情况(所有图片一次成功)
|
||
- **总耗时**: 约 2-4秒(4张图片并发下载)
|
||
- **网络请求**: 4次
|
||
|
||
### 一般情况(有1-2次重试)
|
||
- **总耗时**: 约 5-8秒
|
||
- **网络请求**: 4-8次
|
||
|
||
### 最坏情况(某些图片多次重试)
|
||
- **总耗时**: 约 10-15秒
|
||
- **网络请求**: 10-16次
|
||
- **成功保存**: 3-4张图片
|
||
|
||
## 🎉 优势
|
||
|
||
1. **高成功率**: 双层重试机制大幅提升成功率
|
||
2. **详细日志**: 每次重试都有日志记录,便于排查
|
||
3. **容错处理**: 部分失败不影响整体
|
||
4. **异步执行**: 不阻塞任务完成主流程
|
||
|
||
## 📋 监控指标
|
||
|
||
建议监控以下指标:
|
||
|
||
- ✅ 图片保存总成功率
|
||
- ✅ 每张图片的成功率
|
||
- ✅ 平均重试次数
|
||
- ✅ 平均保存耗时
|
||
- ✅ 失败原因分布
|
||
|
||
---
|
||
|
||
**更新时间**: 2025-11-03
|
||
**重试次数**: 每张图片最多3次
|
||
**重试延迟**: 500毫秒
|
||
**状态**: ✅ 已实现
|
||
|