Files
game_server/IMAGE_SAVE_RETRY_MECHANISM.md

7.8 KiB
Raw Permalink Blame History

完成图片保存重试机制

🔄 重试策略

为了确保4张完成图片都能成功保存系统实现了双层重试机制

📊 重试架构

第一层网络层重试ScriptClient

位置: ScriptClient.getImagePng()

.timeout(Duration.ofSeconds(10))  // 10秒超时
.retry(3)  // 网络失败重试3次立即重试

重试条件:

  • 网络连接失败
  • HTTP错误
  • 超时10秒

重试间隔: 立即重试(无延迟)


第二层业务层重试CompletionImageService

位置: CompletionImageService.downloadAndSaveImage()

.retryWhen(Retry.fixedDelay(3, Duration.ofMillis(500))
    .doBeforeRetry(retrySignal -> {
        long attempt = retrySignal.totalRetries() + 1;
        log.warn("下载图片失败,开始第{}次重试: codeNo={}, imageName={}", 
                attempt, codeNo, imageName);
    })
)

重试条件:

  • 图片下载失败
  • 文件写入失败
  • 任何异常

重试间隔: 500毫秒固定延迟

重试次数: 3次


🎯 完整重试流程

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[保存成功]

📝 日志示例

成功场景(无重试)

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

重试场景(网络抖动)

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字节

最终失败场景

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秒极端情况

🎯 容错处理

部分图片失败不影响整体

即使某张图片下载失败,其他图片依然会保存成功:

{
  "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 字段中:

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

🔍 故障排查

检查某个任务的图片保存情况

# 查看日志
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. 监控重试率

-- 统计图片保存成功率
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. 性能调优

# 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毫秒
状态: 已实现