Azure Functions 的实例正在共享变量?
Instances of Azure Functions are sharing variables?
不确定这个问题是否有意义,但这就是我观察到的。我的 Azure 函数使用 BlobTrigger 来处理上传到 Blob 存储的 PDF 文件。一切正常,直到我一次上传多个 blob,在这种情况下,使用下面的代码我观察到以下内容:
第一个 context.getLogger() 正确记录触发函数的每个 blob。
在 Azure 文件共享中,每个 PDF 文件都被正确保存。
第二个 context.getLogger() 在许多情况下 returns 不正确的结果(来自其他文件之一),就好像我的函数实例之间共享变量一样。请注意,第 [19] 行对于每个 PDF 都是唯一的。
我稍后在我的代码中注意到类似的行为,其中记录了来自错误 PDF 的数据。
编辑:明确地说,我知道当多个实例 运行 并行时,日志不会按顺序排列。但是,当我上传 10 个文件时,行 [19] 没有得到 10 个独特的结果,大多数结果都是重复的,并且当基于 X 我想做 Y 时,这个问题后来在我的代码中变得更糟,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 which has support to load from byte[] https://pdfbox.apache.org/docs/2.0.3/javadocs/index.html?org/apache/pdfbox/pdmodel/PDDocument.html (check the load method overload which accepts byte[]). I have also answered your 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 class 中,您似乎在方法之外的某处声明了 'lines' 为静态,这是此问题的根本原因。跟功能没关系,是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;
}
只是想分享将 host.json
更改为以下内容以停止并发函数调用,似乎已经解决了我的问题:
{
"version": "2.0",
"extensions": {
"queues": {
"batchSize": 1,
"newBatchThreshold": 0
}
}
}
非常感谢@KrishnenduGhosh-MSFT 的帮助。我仍然不确定为什么并发函数调用会导致我遇到的问题,但考虑到我的程序还连接到 SQL 数据库和 Sharepoint 站点(两者都被限制)sequential-processing 是最好的解决方案不管怎样
是的。 Azure 函数调用可以共享变量。我需要 100% 确定所有代码,但看起来 lines
对象被声明为 static
并且它可以在调用之间共享。让我们尝试将 static String[]
更改为 String[]
,看看问题是否消失?
Azure 函数很容易上手,很容易忘记执行环境。您的函数调用并不像它们看起来那样孤立。有一个父线程调用您的函数,静态变量不是“线程安全的”。静态变量代表一个全局状态,因此它是全局可访问的。此外,它不附加任何特定的对象实例。变量的“静态”与它所在的内存 space 有关,而不是它的值。因此,同一变量可从引用它的所有 class 个实例访问。
PS。您已在回答 by reducing concurrency, but that may come at a cost to scalability. I'd recommend load testing that. Also static variables can be useful. Many are thread-safe and you want to use them in Azure functions, such as your httpClient or sqlClient DB connections! Give number three a read, here.
中解决了问题
不确定这个问题是否有意义,但这就是我观察到的。我的 Azure 函数使用 BlobTrigger 来处理上传到 Blob 存储的 PDF 文件。一切正常,直到我一次上传多个 blob,在这种情况下,使用下面的代码我观察到以下内容:
第一个 context.getLogger() 正确记录触发函数的每个 blob。
在 Azure 文件共享中,每个 PDF 文件都被正确保存。
第二个 context.getLogger() 在许多情况下 returns 不正确的结果(来自其他文件之一),就好像我的函数实例之间共享变量一样。请注意,第 [19] 行对于每个 PDF 都是唯一的。
我稍后在我的代码中注意到类似的行为,其中记录了来自错误 PDF 的数据。
编辑:明确地说,我知道当多个实例 运行 并行时,日志不会按顺序排列。但是,当我上传 10 个文件时,行 [19] 没有得到 10 个独特的结果,大多数结果都是重复的,并且当基于 X 我想做 Y 时,这个问题后来在我的代码中变得更糟,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 which has support to load from byte[] https://pdfbox.apache.org/docs/2.0.3/javadocs/index.html?org/apache/pdfbox/pdmodel/PDDocument.html (check the load method overload which accepts byte[]). I have also answered your 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 class 中,您似乎在方法之外的某处声明了 'lines' 为静态,这是此问题的根本原因。跟功能没关系,是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;
}
只是想分享将 host.json
更改为以下内容以停止并发函数调用,似乎已经解决了我的问题:
{
"version": "2.0",
"extensions": {
"queues": {
"batchSize": 1,
"newBatchThreshold": 0
}
}
}
非常感谢@KrishnenduGhosh-MSFT 的帮助。我仍然不确定为什么并发函数调用会导致我遇到的问题,但考虑到我的程序还连接到 SQL 数据库和 Sharepoint 站点(两者都被限制)sequential-processing 是最好的解决方案不管怎样
是的。 Azure 函数调用可以共享变量。我需要 100% 确定所有代码,但看起来 lines
对象被声明为 static
并且它可以在调用之间共享。让我们尝试将 static String[]
更改为 String[]
,看看问题是否消失?
Azure 函数很容易上手,很容易忘记执行环境。您的函数调用并不像它们看起来那样孤立。有一个父线程调用您的函数,静态变量不是“线程安全的”。静态变量代表一个全局状态,因此它是全局可访问的。此外,它不附加任何特定的对象实例。变量的“静态”与它所在的内存 space 有关,而不是它的值。因此,同一变量可从引用它的所有 class 个实例访问。
PS。您已在回答