0

我有一个用于某个项目的简单架构,它有点像这样:

  1. 用户使用 ASP.NET MVC 请求文件
  2. 为所述文件检查本地缓存,如果缓存中不存在该文件,则从 Azure Blob 存储中提取该文件。
  3. 至此,该文件肯定在我的服务器上,并且我知道路径。
  4. 我使用第三方库打开文件,我为它提供路径,然后它返回一个类结构,我用它来为用户创建一个视图。

这在 99% 的时间里都有效。文件要么在缓存中找到,要么下载,然后使用第三方代码打开,然后以漂亮的视图呈现给用户。

但是,我可以复制一组奇怪的情况,这会导致我的生产服务器彻底崩溃。

它们是:

  • 在缓存中找不到文件
  • 文件从 Azure 下载
  • 打开文件时第三方(不安全)库崩溃
  • 带上服务器。

我知道我在使用不安全的库时遇到了一些麻烦,但最奇怪的是,如果我第二次尝试,该文件现在将在缓存中,因为第一次正确使用了 Azure,Azure 没有被命中,该文件将成功打开。

第三方库本质上认为从 Azure 下载的文件已损坏,但是当完全相同的代码尝试在没有 Azure 参与的情况下打开完全相同的文件时,它打开没有问题。

所以我最初责怪 Azure,也许我没有正确关闭文件。我已经检查过了,我用来获取文件的文件流肯定是关闭的(它被包装在 using 语句中)。

代码如下,(forceRefresh 是一个我可以设置为始终跳过本地缓存的标志)。路径设置为我服务器上的某个位置 ~/tmp

if (!File.Exists(path) || forceRefresh)
{
   CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
   CloudBlobContainer blobContainer = blobClient.GetContainerReference("mycontainer");

   ICloudBlob blob = blobContainer.GetBlockBlobReference("myblob");
   using (var filestream = new FileStream(path,FileMode.Create,FileAccess.ReadWrite,FileShare.Read))
   {
        blob.DownloadToStream(filestream);
   }

}

更奇怪的是,我重复了我的测试,这一次,在崩溃之后,我从缓存中删除了文件,这样 Azure 就会再次被击中。它是,文件打开而不会导致崩溃。

所以它似乎只在第一次打开从 Azure 下载的文件时发生——我可以通过回收应用程序池来按需导致错误。

有人对我如何调试这个有任何建议吗?我无法在我的本地开发机器上复制。

编辑:理查德·特纳对反馈的回答

我不相信 Blob 检索代码是导致异常的原因。这样做的原因是,如果 Web 应用程序确实崩溃,这是在文件下载之后。我什至可以验证文件没有损坏,因为我可以在后续重试时打开它。

关于“不安全”,您所说的完全正确-我有执行 PInvoke 的包装器代码-但是,它没有实现 IDisposable,这是我将立即研究的内容。至于性能,目前我并不担心。

关于重组我的代码,我描述的“缓存”实际上是磁盘上的一组文件,所以本质上我已经有了你推荐的结构。第 3 方库仅接受文件路径作为输入,因此我必须遵循该路线。

回答您的最后问题:

  1. 将 blob 作为流读取是我可以看到的唯一可以读取它的方法吗?我没有看到任何 API 方法可以将其作为 byte[] 或其他任何东西。

  2. FileStream 不需要读/写,但改变它并没有改善。

4

2 回答 2

0

您应该对此类事情使用异常处理:

      try
        {
            //connection here
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            // will give you the error if no connection and you can get an idea why it crashes
        }
于 2013-02-26T16:35:20.340 回答
0

很难确定出了什么问题,因为您没有发布足够的代码,例如,了解您如何实例化/销毁您的 3rd 方组件的实例以及您如何将数据传递给所述组件。

但是,这里有几点需要考虑:

默契是正确的:您的应用程序正在崩溃,因为它抛出了未处理的异常。假设任何与存储服务交互的操作都会失败。如果他们还没有,他们会。因此,实现一个异常处理程序来包装您的 blob 检索代码并在必要时实现重试语义。您可能想要添加ELMAH日志记录,以便在您的应用代码中记录崩溃和故障。

我不太明白您所说的 3rd 方库“不安全”是什么意思。您的意思是“不安全”,因为它是您必须 PInvoke 进入的本机库?

根据您的代码如何实例化/销毁第 3 方组件,您可能会发现该组件在使用后仍保留在内存中,并且它可能会保持一些被损坏/混淆的状态。如果您必须使用这样的组件,请考虑编写您自己的 PInvoke 包装器,该包装器也支持 IDisposable 并在您完成后强制卸载组件。但是请注意,这可能会导致性能下降。

要考虑的另一件事:也许您应该稍微重新组织您的代码,以便第 3 方组件只从磁盘加载文件。

  1. 如果文件不在磁盘上,则从 Azure 加载它(使用必要的异常处理程序进行包装操作)并将其存储在磁盘上
  2. 从磁盘加载文件内容并传递给第 3 方组件

此外,您的代码中还有一些其他内容需要查看:

  1. 您是否有任何理由将 blob 作为流而不是 byte[] 或字符串来读取?
  2. 您创建读/写文件流有什么原因吗?如果您不写入流,请考虑以只读模式打开流。

更新 2013-02-28 @ 16:47 PST - 回复以上 KingCronus 的更新:

CloudBlobContainer.GetBlockBlobReference(...) 返回一个 CloudBlockBlob 对象。CloudBlockBlob 对象具有DownloadByteArray()DownloadText()以及重要的是DownloadToFile()的方法。后一个 API 可能有助于完全缓解文件损坏问题!

您正在 PInvoking 的本机组件完全有可能正在破坏 IIS 控制之外的正在运行的进程。

如果它崩溃是因为其输入数据(即文件)已损坏,那么请考虑确定您是否可以预处理数据以使其符合组件的要求。

如果组件继续崩溃,尤其是因为其内部状态被破坏,您可能需要考虑通过例如创建托管组件实例的 Windows 服务来将组件与 IIS 隔离。这样的服务如果失败可以自动重启。您可以通过 WCF/NamedPipes/etc 与托管您的组件的服务进行通信。并来回整理数据。但是,我认为如果可能的话,我宁愿考虑完全替换该组件,而不是引入这样的最后手段机制!

于 2013-02-26T22:14:17.467 回答