问题描述
不确定该问题是否有意义,但这是我正在观察的内容。我的Azure功能使用BlobTrigger来处理上传到Blob存储的PDF文件。一切正常,直到我一次上传几个Blob,在这种情况下,使用下面的代码,我观察到以下内容:
编辑:要清楚,我知道当多个实例并行运行时,日志不会井井有条。但是,当我上载10个文件时,并没有获得10个行的唯一结果[19],而是大多数结果是重复的,并且此后在基于XI的情况下我的代码使问题更严重,并且在10个调用中有9个产生垃圾数据。
Main.class
public class main {
@FunctionName("veninv")
@StorageAccount("Storage")
public void blob(
@BlobTrigger(
name = "blob",dataType = "binary",path = "veninv/{name}")
byte[] content,@BindingName("name") String blobname,final ExecutionContext context
) {
context.getLogger().info("BlobTrigger by: " + blobname + "(" + content.length + " bytes)");
//Writing byte[] to a file in Azure Functions file storage
File tempfile = new File (tempdir,blobname);
OutputStream os = new FileOutputStream(tempfile);
os.write(content);
os.close();
String[] lines = Pdf.getLines(tempfile);
context.getLogger().info(lines[19]);
}
}
Pdf.class
public static String[] getLines(File PDF) throws Exception {
PDDocument doc = PDDocument.load(PDF);
PDFTextStripper pdfStripper = new PDFTextStripper();
String text = pdfStripper.getText(doc);
lines = text.split(System.getProperty("line.separator"));
doc.close();
return lines;
}
我不太了解这里发生了什么,因此希望获得帮助。
解决方法
不,很难相信功能会带来如此严重的问题。我发现一些潜在的问题可能会导致您的情况:
- 您确定每次都将每个文件上传到不同的唯一Blob吗?您可以通过记录blobname参数来进行检查。
- 由于您将文件存储在临时目录
File tempfile = new File (tempdir,blobname);
中,因此,如果blob名称与#1中提到的名称相同,则它将以最后一次写入获胜而被覆盖。如果可以直接从字节或流构造pdf,则可以考虑这样做,而不是在文件系统中创建中间文件。如果我没看错,您使用的是PDFBox,它支持从byte [] https://pdfbox.apache.org/docs/2.0.3/javadocs/index.html?org/apache/pdfbox/pdmodel/PDDocument.html加载(检查接受byte []的加载方法重载)。我也回答了您与此有关的another question。 - 检查是否有导致此问题的静态字段。
- 您不需要使用要引入的单独队列。尽管在实际问题已解决的情况下根本不需要它,但Blob触发器已使用内部队列,默认并发值为24,但是您可以在host.json中对其进行配置。 https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob-trigger?tabs=java#concurrency-and-memory-usage
更新:
就像在您的pdf类中,您在方法之外的某处将“ lines”声明为static,这是此问题的根本原因。与功能无关,而是 static :)
的魔鬼下面是正确的代码(请注意,“ lines”变量现在在方法本地):
public static String[] getLines(File PDF) throws Exception {
PDDocument doc = PDDocument.load(PDF);
PDFTextStripper pdfStripper = new PDFTextStripper();
String text = pdfStripper.getText(doc);
String[] lines = text.split(System.getProperty("line.separator"));
doc.close();
return lines;
}
,
是的。 Azure函数调用可以共享变量。我需要确保所有代码都是100%确定的,但是看起来lines
对象被声明为static
,并且可以在调用之间共享。让我们尝试从static String[]
更改为String[]
,看看问题是否消失了吗?
Azure函数易于起步,很容易忘记执行环境。您的函数调用并不像它们看起来那样孤立。有一个父线程调用您的函数,并且静态变量不是“线程安全的”。静态变量表示全局状态,因此可以全局访问。而且,它不附带任何特定的对象实例。变量的“静态性”与其所位于的内存空间有关,而不是其值。因此,可以在引用该变量的所有类实例中访问该变量。
PS。您已经通过减少并发解决了答案here中的问题,但这可能会损害可伸缩性。我建议进行负载测试。静态变量也可能有用。许多都是线程安全的,您想在Azure函数中使用它们,例如httpClient或sqlClient DB连接!读三号here。
,只想与他人分享将host.json
更改为以下内容,以停止并发函数调用,似乎已经解决了我的问题:
{
"version": "2.0","extensions": {
"queues": {
"batchSize": 1,"newBatchThreshold": 0
}
}
}
非常感谢@ KrishnenduGhosh-MSFT的帮助。我仍然不确定为什么并发函数调用会导致我遇到的问题,但是考虑到我的程序还连接到SQL数据库和Sharepoint站点(均受限制),顺序处理是最佳解决方案。