task-154: 临时目录磁盘容量告警(aiimage.storage.capacity-warn-bytes 阈值、告警含当前容量/阈值、10 分钟频率限制、测量失败容忍不阻塞)+ 8 条测试

This commit is contained in:
2026-09-02 06:40:20 +08:00
parent 57f75b8bc3
commit 87a6d20f88
3 changed files with 190 additions and 0 deletions
@@ -14,6 +14,8 @@ public class StorageProperties {
private long sourceRetentionHours = 24;
private long resultRetentionHours = 24;
private long transientPayloadRetentionHours = 72;
/** 临时目录容量告警阈值(字节),超过时记录告警日志(task-154)。 */
private long capacityWarnBytes = 50L * 1024 * 1024 * 1024;
public String getLocalTempDir() {
String configured = localTempDir == null ? "" : localTempDir.trim();
@@ -0,0 +1,78 @@
package com.nanri.aiimage.modules.file.service;
import cn.hutool.core.io.FileUtil;
import com.nanri.aiimage.config.StorageProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
/**
* 临时目录磁盘容量告警(task-154)。
*
* 容量超过阈值时记录告警日志(当前容量/阈值);告警带频率限制(默认每 10 分钟
* 至多一次,防刷屏);测量失败或目录缺失时仅记日志,不抛异常、不阻塞业务。
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TempDirCapacityMonitor {
private static final long WARN_MIN_INTERVAL_MILLIS = 10 * 60 * 1000L;
private final StorageProperties storageProperties;
private volatile long lastWarnAtMillis = 0L;
@Scheduled(cron = "${aiimage.storage.capacity-warn-cron:0 */30 * * * *}")
public void checkTempDirCapacity() {
File tempDir = FileUtil.file(storageProperties.getLocalTempDir());
if (!tempDir.exists() || !tempDir.isDirectory()) {
return;
}
long usage = measureUsageBytes(tempDir);
if (usage < 0) {
log.warn("temp dir capacity measurement failed dir={}", tempDir.getAbsolutePath());
return;
}
long threshold = storageProperties.getCapacityWarnBytes();
if (usage > threshold) {
warnWithRateLimit(tempDir, usage, threshold);
}
}
/** 测量目录总字节数;失败返回 -1(不抛)。 */
public long measureUsageBytes(File dir) {
if (dir == null || !dir.isDirectory()) {
return -1L;
}
try (Stream<Path> paths = Files.walk(dir.toPath())) {
return paths.mapToLong(path -> {
try {
return Files.isRegularFile(path) ? Files.size(path) : 0L;
} catch (IOException ex) {
return 0L;
}
}).sum();
} catch (IOException ex) {
log.warn("temp dir capacity walk failed dir={}", dir.getAbsolutePath(), ex);
return -1L;
}
}
void warnWithRateLimit(File tempDir, long usage, long threshold) {
long now = System.currentTimeMillis();
if (now - lastWarnAtMillis < WARN_MIN_INTERVAL_MILLIS) {
return;
}
lastWarnAtMillis = now;
log.warn("temp dir capacity warning: usage={} bytes, threshold={} bytes, dir={}",
usage, threshold, tempDir.getAbsolutePath());
}
}