fix(内存): 两处无界内存风险加固——brand-check 线程池改有界队列+CallerRuns(大批量检查不再无界堆积 OOM);OSS readObjectBytes 加 20MB 读取上限防大对象误用 OOM
This commit is contained in:
+8
-2
@@ -44,8 +44,14 @@ public class BrandCheckClient {
|
||||
this.externalCallMetrics = externalCallMetrics;
|
||||
}
|
||||
|
||||
private final ExecutorService checkExecutor = Executors.newFixedThreadPool(
|
||||
BRAND_CHECK_CONCURRENCY, namedThreadFactory("brand-check"));
|
||||
// 有界队列 + CallerRuns:大批量(几千品牌)检查时队列洪峰不无界堆内存,
|
||||
// 队列打满时提交线程自己执行一个检查任务,上游天然限速。
|
||||
private final ExecutorService checkExecutor = new java.util.concurrent.ThreadPoolExecutor(
|
||||
BRAND_CHECK_CONCURRENCY, BRAND_CHECK_CONCURRENCY,
|
||||
60L, java.util.concurrent.TimeUnit.SECONDS,
|
||||
new java.util.concurrent.LinkedBlockingQueue<>(1000),
|
||||
namedThreadFactory("brand-check"),
|
||||
new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());
|
||||
|
||||
@PreDestroy
|
||||
void shutdownCheckExecutor() {
|
||||
|
||||
+22
-1
@@ -41,6 +41,8 @@ public class OssStorageService {
|
||||
private static final String DIGITAL_HUMAN_PREFIX = "digital-human/versions/";
|
||||
private static final String SOFTWARE_VERSION_PREFIX = "nanri-image/versions/";
|
||||
private static final String LEGACY_MINIO_ENDPOINT = "http://47.110.241.161:9000";
|
||||
/** readObjectBytes 兜底读取上限(20MB):防止大对象一次性读入内存导致 OOM。 */
|
||||
private static final long DEFAULT_READ_MAX_BYTES = 20L * 1024 * 1024;
|
||||
|
||||
private final OssProperties ossProperties;
|
||||
private final MinioClient minioClient;
|
||||
@@ -248,13 +250,32 @@ public class OssStorageService {
|
||||
}
|
||||
|
||||
public byte[] readObjectBytes(String bucket, String objectKey) {
|
||||
// 防御性上限:防止被误用到大对象时一次性读入内存导致 OOM(当前仅模板下载场景,模板为 xlsx KB 级)
|
||||
return readObjectBytes(bucket, objectKey, DEFAULT_READ_MAX_BYTES);
|
||||
}
|
||||
|
||||
public byte[] readObjectBytes(String bucket, String objectKey, long maxBytes) {
|
||||
String normalizedBucket = requireStorageName(bucket, "bucket");
|
||||
String normalizedObjectKey = requireStorageName(objectKey, "objectKey");
|
||||
try (var stream = buildClient().getObject(GetObjectArgs.builder()
|
||||
.bucket(normalizedBucket)
|
||||
.object(normalizedObjectKey)
|
||||
.build())) {
|
||||
return stream.readAllBytes();
|
||||
byte[] buffer = new byte[1024 * 1024];
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
long total = 0;
|
||||
int read;
|
||||
while ((read = stream.read(buffer)) != -1) {
|
||||
total += read;
|
||||
if (total > maxBytes) {
|
||||
throw new IllegalArgumentException("对象过大,超过读取上限 " + maxBytes + " 字节: "
|
||||
+ normalizedBucket + "/" + normalizedObjectKey);
|
||||
}
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
return out.toByteArray();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw storageFailure("read", normalizedBucket + "/" + normalizedObjectKey, ex);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user