哈喽,各位开发者伙伴们! 在日常开发中,我们经常会遇到一些看似诡异的问题。程序明明上次运行得好好的,怎么这次启动就感觉哪里不对劲? 最近,我就遇到了一个关于 Spring Boot 应用启动时处理阿里云 OSS 图片缩略图的任务表现异常的情况。每次应用启动,它都“勤勤恳恳”地把一个文件夹里的所有图片缩略图重新生成并上传了一遍,即使这些缩略图早就存在了!这不仅拖慢了启动速度 ⏳,还造成了不必要的 OSS 请求和覆盖操作 。
经过一番排查,最终定位并解决了问题。今天,就和大家分享一下这次“破案”的全过程,希望能给大家带来一些启发。✨
首先,我们用一个表格来梳理一下遇到的情况:
方面 | 描述 | 表情符号 |
---|---|---|
问题表现 | Spring Boot 应用每次启动时,后台任务都会处理 fake-strategy 目录下的所有图片,生成并上传缩略图。 |
⏳ |
日志症状 | 控制台充斥着大量 Generating thumbnails for image... 、开始使用 ossClient 上传文件... (thumb_ 和 medium_ ) 日志。 |
|
初步怀疑 | 是不是任务调度逻辑有问题?或者是有新文件加入了? | ❓ |
核心疑点 | 为什么不检查缩略图是否已存在就直接生成和上传? | |
根本原因 | ThumbnailService 在触发生成逻辑前,未检查目标缩略图文件 (thumb_... , medium_... ) 是否已存在于 OSS。 |
❌ |
解决方案 | 在 ThumbnailService 中,调用生成方法前,增加使用 ossClient.doesObjectExist() 检查目标文件存在性的逻辑。 |
✅ |
验证方法 | 开启 ThumbnailService 的 DEBUG 日志,观察是否打印跳过信息,以及原有的生成/上传 INFO 日志是否消失。 |
下面是原始代码逻辑的工作流程图,清晰地展示了为什么会做重复的工作:
(注意:流程图中的所有文本都按要求用双引号包裹)
可以看到,从 D 到 E 这一步,只要判断是原始图片,就直接去执行 F 及后续步骤了,完全没有检查 K 和 I 对应的文件是否已经存在于 OSS!
为了解决这个问题,我们在 ThumbnailService
的循环中,决定处理一个原始图片之前,加入了关键的存在性检查。
修改后的核心代码片段 (ThumbnailService.java
):
// ... (循环和过滤逻辑) ...
for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
String imageKey = objectSummary.getKey();
if (imageKey.endsWith("/") || imageKey.contains("thumb_") || imageKey.contains("medium_")) { // 优化了过滤
continue;
}
// ---- 新增:检查目标缩略图是否存在 ----
String thumbKey = generateThumbnailKey(imageKey, "thumb_");
String mediumKey = generateThumbnailKey(imageKey, "medium_");
boolean thumbExists = false;
boolean mediumExists = false;
try {
// 使用同一个 ossClient 实例进行检查
thumbExists = ossClient.doesObjectExist(ossBucketName, thumbKey);
mediumExists = ossClient.doesObjectExist(ossBucketName, mediumKey);
} catch (OSSException oe) {
logger.error("OSS error checking existence for key {}: {}", imageKey, oe.getErrorMessage());
failureCount.incrementAndGet();
logFailedImage(imageKey, "OSS error checking existence: " + oe.getErrorMessage());
continue; // 出错则跳过
} catch (Exception ex) {
logger.error("Error checking existence for key {}: {}", imageKey, ex.getMessage());
failureCount.incrementAndGet();
logFailedImage(imageKey, "Error checking existence: " + ex.getMessage());
continue; // 出错则跳过
}
// 如果两个缩略图都已经存在,则跳过
if (thumbExists && mediumExists) {
logger.debug("Thumbnails already exist for {}, skipping generation.", imageKey); // DEBUG 日志用于验证
continue; // !! 关键的跳过 !!
}
// ---- 检查结束 ----
// 只有在需要时才创建任务
tasks.add(() -> {
logger.info("Generating thumbnails for image: {}", imageKey); // 这个 INFO 日志现在只会在需要生成时打印
BaseResult result = ossUtil.generateThumbnailsForExistingImage(imageKey, 1); // 传入 ossClient 实例会更好
// ... (处理结果) ...
});
// ... (其他逻辑) ...
}
修正后的检查与执行时序 (Mermaid 时序图):
这个时序图展示了增加检查后的交互过程:
(注意:时序图中的文本按要求没有加双引号)
代码改完就大功告成了吗?不!验证是必不可少的环节。我们需要确认修改确实生效了。
logback-spring.xml
中为 ThumbnailService
添加
配置。DEBUG - Thumbnails already exist for ..., skipping generation.
。最终完成时,successful
计数为 0 或接近 0。INFO - Generating thumbnails for image: ...
以及后续的大量 OssUtil
上传日志。经过验证,日志完全符合预期!大量的 DEBUG skipping 日志出现了,而 INFO 级别的生成和上传日志几乎消失了,最终成功计数为 0。问题解决!
这次调试虽然花了点时间,但也收获了不少:
OssUtil
内部频繁创建/销毁 OSS 客户端。对于批量任务,共享客户端实例是进一步提升性能的方向 ✨。希望这次的调试经历分享对你有所帮助!如果你也遇到过类似的问题,或者有更好的解决思路,欢迎在评论区交流!