打开文件时导致服务器崩溃的原因是什么?

时间:2013-02-26 16:14:49

标签: c# iis azure

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

  1. 用户使用ASP.NET MVC请求文件
  2. 检查所述文件的本地缓存,如果缓存中不存在,则从Azure Blob存储中提取该文件。
  3. 到目前为止,该文件肯定在我的服务器上,我知道路径。
  4. 我使用第三方库打开文件,我提供路径,然后返回一个类结构,我用它来为用户创建一个View。
  5. 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);
       }
    
    }
    

    Weirder仍然,我重复我的测试,这次,在崩溃后,我从缓存中删除文件,以便再次点击Azure。它是,并且文件打开而不会导致崩溃。

    所以它似乎只是第一次打开从Azure下载的文件 - 我只需通过回收应用程序池就可以导致错误。

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

    编辑:Richard Turner的反馈意见

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

    你对'不安全'的说法是完全正确的 - 我有执行PInvoke的包装代码 - 然而,它没有实现IDisposable,这是我将立即研究的内容。至于表现,此时我并不担心。

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

    回答你的最后问题:

    1. 将blob作为流读取是我能看到的唯一可读方式吗?我没有看到任何API方法将其作为byte []或其他任何内容获取。

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

2 个答案:

答案 0 :(得分: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
        }

答案 1 :(得分:0)

  

由于你没有发帖,很难确定出了什么问题   例如,足够的代码可以理解你的样子   实例化/销毁第三方组件的实例以及如何实例化   你将数据传递给所说的组件。

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

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

我不太明白第三方图书馆的“不安全”是什么意思。你的意思是“不安全”,因为它是你必须进入PInvoke的本地图书馆吗?

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

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

  1. 如果文件不在磁盘上,则从Azure加载(使用必要的异常处理程序进行包装操作)并将其存储在磁盘上
  2. 从磁盘加载文件内容并传递给第三方组件
  3. 此外,您的代码中还有其他一些内容:

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

      更新2013-02-28 @ 16:47太平洋标准时间 - 回复上面的KingCronus更新:

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

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

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

      如果组件继续崩溃,特别是因为其内部状态已损坏,您可能需要考虑通过创建承载组件实例的Windows服务来隔离IIS中的组件。如果失败,可以自动重启这样的服务。您可以通过WCF / NamedPipes /等与托管组件的服务进行通信。来回传递数据。但是,我认为我宁愿考虑完全替换组件,而不是像这样引入最后的机制!