1

不确定这个问题是否有意义,但这是我所观察到的。我的 Azure 函数使用 BlobTrigger 来处理上传到 Blob 存储的 PDF 文件。一切正常,直到我一次上传几个 blob,在这种情况下,使用下面的代码我观察到以下内容:

  • 第一个 context.getLogger() 正确记录触发函数的每个 blob。

  • 在 Azure 文件共享中,每个 PDF 文件都已正确保存。

  • 在许多情况下,第二个 context.getLogger() 返回不正确的结果(来自其他文件之一),就好像在我的函数实例之间共享变量一样。请注意,对于每个 PDF,行 [19] 都是唯一的。

  • 我稍后在我的代码中注意到类似的行为,其中记录了来自错误 PDF 的数据。

编辑:要清楚,我知道当多个实例并行运行时日志不会按顺序排列。但是,当我上传 10 个文件时,不是为行 [19] 获得 10 个唯一结果,而是大多数结果是重复的,并且当基于 XI 想要执行 Y 时,这个问题稍后在我的代码中恶化,并且 10 次调用中有 9 次产生垃圾数据。

主类

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;
   }

我真的不明白这里发生了什么,所以希望得到一些帮助。

4

3 回答 3

4

是的。Azure 函数调用可以共享变量。我需要看到所有代码都是 100% 确定的,但看起来lines对象被声明为static并且可以跨调用共享。让我们尝试从 a 更改static String[]String[],看看问题是否消失?

Azure 函数很容易起步,很容易忘记执行环境。您的函数调用并不像它们看起来那样孤立。有一个父线程调用您的函数,静态变量不是“线程安全的”。静态变量代表一个全局状态,因此它是全局可访问的。此外,它没有附加任何特定的对象实例。变量的“静态性”与它所在的内存空间有关,而不是它的值。因此,可以从引用它的所有类实例访问相同的变量。

PS。您已经通过减少并发性解决了您的答案中的问题但这可能会以可伸缩性为代价。我建议对此进行负载测试。静态变量也很有用。许多都是线程安全的,您想在 Azure 函数中使用它们,例如您的 httpClient 或 sqlClient DB 连接!在这里阅读第三个。

于 2020-08-29T22:07:44.323 回答
1

不,很难相信函数会出现如此严重的问题。在您的情况下,我看到了一些可能导致这种情况的潜在问题:

  1. 您确定每次都为每个文件上传到不同的唯一 blob 吗?您可以通过记录 blobname 参数进行检查。
  2. 由于您将文件存储在 temp directoryFile tempfile = new File (tempdir, blobname);中,如果 blob 名称与 #1 中提到的相同,它将被最后一次写入胜利覆盖。如果可以直接从字节或流构造 pdf,则可以考虑这样做,而不是在文件系统中创建中间文件。如果我没记错的话,您使用的PDFBox支持从字节 [] https://pdfbox.apache.org/docs/2.0.3/javadocs/index.html?org/apache/pdfbox/pdmodel/PDDocument 加载。 html(检查接受 byte[] 的加载方法重载)。我还回答了您的另一个与此相关的问题。
  3. 检查您是否有导致此问题的静态字段。
  4. 您不需要使用您正在考虑引入的单独队列。虽然如果实际问题得到解决,您根本不需要它,但 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' 变量现在是该方法的本地变量):

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;
   }
于 2020-08-29T08:46:55.147 回答
0

只是想分享更改host.json为以下内容,以停止并发函数调用,似乎已经解决了我的问题:

{
    "version": "2.0",
    "extensions": {
        "queues": {
            "batchSize": 1,
            "newBatchThreshold": 0
        }
    }
}

非常感谢 @KrishnenduGhosh-MSFT 的帮助。我仍然不确定为什么并发函数调用会导致我遇到的问题,但考虑到我的程序还连接到 SQL 数据库和 Sharepoint 站点(两者都受到限制)顺序处理无论如何都是最好的解决方案。

于 2020-08-29T18:05:05.260 回答