用java编写的Azure函数抛出FailureException:OutOfMemoryError:java堆空间堆栈,同时解压缩文件大小>;80MB
我有一个用java编写的Azure函数,它将侦听Azure上的队列消息,队列消息具有Azure blob容器上zip文件的路径,一旦收到队列消息,它将从Azure上的路径位置获取zip文件并解压缩到Azure上的容器。它适用于小文件,但大于80MB时会显示用java编写的Azure函数抛出FailureException:OutOfMemoryError:java堆空间堆栈,同时解压缩文件大小>;80MB,java,unzip,azure-blob-storage,azure-function-app,azure-blob-trigger,Java,Unzip,Azure Blob Storage,Azure Function App,Azure Blob Trigger,我有一个用java编写的Azure函数,它将侦听Azure上的队列消息,队列消息具有Azure blob容器上zip文件的路径,一旦收到队列消息,它将从Azure上的路径位置获取zip文件并解压缩到Azure上的容器。它适用于小文件,但大于80MB时会显示FailureException:OutOfMemoryError:Java heap spaceStackexception。我的代码如下 @FunctionName("queueprocessor") public vo
FailureException:OutOfMemoryError:Java heap spaceStack
exception。我的代码如下
@FunctionName("queueprocessor")
public void run(@QueueTrigger(name = "msg",
queueName = "queuetest",
dataType = "",
connection = "AzureWebJobsStorage") Details message,
final ExecutionContext executionContext,
@BlobInput(name = "file",
dataType = "binary",
connection = "AzureWebJobsStorage",
path = "{Path}") byte[] content) {
executionContext.getLogger().info("PATH: " + message.getPath());
CloudStorageAccount storageAccount = null;
CloudBlobClient blobClient = null;
CloudBlobContainer container = null;
try {
String connectStr = "DefaultEndpointsProtocol=https;AccountName=name;AccountKey=mykey;EndpointSuffix=core.windows.net";
//unique name of the container
String containerName = "output";
// Config to upload file size > 1MB in chunks
int deltaBackoff = 2;
int maxAttempts = 2;
BlobRequestOptions blobReqOption = new BlobRequestOptions();
blobReqOption.setSingleBlobPutThresholdInBytes(1024 * 1024); // 1MB
blobReqOption.setRetryPolicyFactory(new RetryExponentialRetry(deltaBackoff, maxAttempts));
// Parse the connection string and create a blob client to interact with Blob storage
storageAccount = CloudStorageAccount.parse(connectStr);
blobClient = storageAccount.createCloudBlobClient();
blobClient.setDefaultRequestOptions(blobReqOption);
container = blobClient.getContainerReference(containerName);
container.createIfNotExists(BlobContainerPublicAccessType.CONTAINER, new BlobRequestOptions(), new OperationContext());
ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(content));
ZipEntry zipEntry = zipIn.getNextEntry();
while (zipEntry != null) {
executionContext.getLogger().info("ZipEntry name: " + zipEntry.getName());
//Getting a blob reference
CloudBlockBlob blob = container.getBlockBlobReference(zipEntry.getName());
ByteArrayOutputStream outputB = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n;
while ((n = zipIn.read(buf, 0, 1024)) != -1) {
outputB.write(buf, 0, n);
}
// Upload to container
ByteArrayInputStream inputS = new ByteArrayInputStream(outputB.toByteArray());
blob.setStreamWriteSizeInBytes(256 * 1024); // 256K
blob.upload(inputS, inputS.available());
executionContext.getLogger().info("ZipEntry name: " + zipEntry.getName() + " extracted");
zipIn.closeEntry();
zipEntry = zipIn.getNextEntry();
}
zipIn.close();
executionContext.getLogger().info("FILE EXTRACTION FINISHED");
} catch(Exception e) {
e.printStackTrace();
}
}
详细信息消息
具有ID和文件路径,路径作为输入提供给@BlobInput(…,path={path},…)
。根据我的分析,我觉得@BlobInput
正在将完整的文件加载到内存中,这就是我从内存错误中得到的原因。如果我是对的,请告诉我还有什么办法可以避免吗?。因为将来文件大小可能会达到2GB。如果在解压代码中有任何错误,请告诉我。谢谢。我将@Joachimauer的建议总结如下
当我们使用Azure Function blob存储绑定来处理java Function应用程序中的Azure blob内容时,它会将整个内容保存在内存中。使用它来处理大文件,我们可能会遇到OutOfMemoryError
。因此,如果我们想要处理大型azure blob,我们应该使用blob sdk打开一个输入流,然后使用该流处理内容
比如说
SDK
com.azure
有关更多详细信息,请参阅。我将@Joachimauer的建议总结如下
当我们使用Azure Function blob存储绑定来处理java Function应用程序中的Azure blob内容时,它会将整个内容保存在内存中。使用它来处理大文件,我们可能会遇到OutOfMemoryError
。因此,如果我们想要处理大型azure blob,我们应该使用blob sdk打开一个输入流,然后使用该流处理内容
比如说
SDK
com.azure
有关更多详细信息,请参阅。您同时将zip文件(在内容中)和解压缩文件(在输出中)完全保存在内存中。我不知道是否有办法将内容
作为流获取,但至少解压后的文件本身,您应该能够直接从流获取,而不是完全解压(假设CloudBlockBlob可以在不将其完全加载到内存的情况下使用流)。谢谢,我刚刚设法直接从URL流中获取内容
,效果很好。@JoachimSauer您能将您的建议作为解决方案发布吗?它可能会帮助更多有类似问题的人。您可以同时将zip文件(在内容中
)和解压缩文件(在输出中
)完全保存在内存中。我不知道是否有办法将内容
作为流获取,但至少解压后的文件本身,您应该能够直接从流获取,而不是完全解压(假设CloudBlockBlob可以在不将其完全加载到内存的情况下使用流)。谢谢,我刚刚设法直接从URL流中获取内容
,效果很好。@JoachimSauer您能将您的建议作为解决方案发布吗?它可能会帮助更多有类似问题的人。谢谢你的全部代码。您能告诉我您尝试的文件大小以及azure或本地计算机上的堆大小吗。@brijeshPatil我的zip文件是189MB。在我的测试环境中,初始heapsize是134217728,最大heapsize是2118123520。好的,谢谢。真的很有帮助。@brijeshPatil如果对你有帮助,你可以吗?Iy可能会帮助更多有类似问题的人。我需要在azure环境中测试这一点。由于azure上的堆大小非常小,我们将对其进行测试,然后肯定会接受它。感谢您提供完整的代码。您能告诉我您尝试的文件大小以及azure或本地计算机上的堆大小吗。@brijeshPatil我的zip文件是189MB。在我的测试环境中,初始heapsize是134217728,最大heapsize是2118123520。好的,谢谢。真的很有帮助。@brijeshPatil如果对你有帮助,你可以吗?Iy可能会帮助更多有类似问题的人。我需要在azure环境中测试这一点。因为azure上的堆大小非常小,所以我们将测试它,然后肯定会接受它。
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.9.0</version>
</dependency>
String accountName="";
String accountKey="";
StorageSharedKeyCredential sharedKeyCredential =
new StorageSharedKeyCredential(accountName, accountKey);
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
.credential(sharedKeyCredential)
.endpoint("https://" + accountName + ".blob.core.windows.net")
.buildClient();
BlobContainerClient desContainerClient = blobServiceClient.getBlobContainerClient("output");
BlobContainerClient sourceContainerClient = blobServiceClient.getBlobContainerClient("upload");
BlobInputStreamOptions option = new BlobInputStreamOptions();
//The size of each data chunk returned from the service
option.setBlockSize(1024*1024);
ZipInputStream zipInput = null;
try {
zipInput= new ZipInputStream( sourceContainerClient.getBlobClient("<read file name deom queue message>").openInputStream(option));
ZipEntry zipEntry= zipInput.getNextEntry();
while(zipEntry != null){
System.out.println("ZipEntry name: " + zipEntry.getName());
BlobOutputStream outputB = desContainerClient.getBlobClient(zipEntry.getName()).getBlockBlobClient().getBlobOutputStream();
byte[] bytesIn = new byte[1024*1024];
int read = 0;
while ((read = zipInput.read(bytesIn)) != -1) {
outputB.write(bytesIn, 0, read);
}
outputB.flush();
outputB.close();
zipInput.closeEntry();
zipEntry =zipInput.getNextEntry();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
zipInput.close();
} catch (IOException e) {
e.printStackTrace();
}
}