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