Files
game_server/IMAGE_SAVE_RETRY_MECHANISM.md

278 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 完成图片保存重试机制
## 🔄 重试策略
为了确保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毫秒
**状态**: ✅ 已实现