1

背景:

每年我们都会运行并归档来自特定 ASP.Net 应用程序的所有报告。该档案提供了系统数据在一年中特定时间的“快照”。我们使用 .Net Forms 构建了一个 GUI,它使用 System.Net.WebClient 调用报表服务器并将报表下载到特定目录。这在过去运作良好。

今年我们将 Excel 文件包含在我们的档案中。Excel 文件由 .aspx 页面(Windows Sever 2003、IIS6、.Net 4.0)创建,该页面接受查询字符串,然后返回 Excel 文件。这适用于 100 个左右的 Excel 文件,但之后我们开始遇到问题。我们每年归档大约 300,000 个文件。

力学:

我们使用 WebClient.DownloadFileAsync 来拉取我们的文件,以免锁定 UI 线程。我们依靠 WebClient.DownloadFileCompleted 事件来告诉我们每个文件何时完成下载。当提出 DownloadFileCompleted 时,我们开始下载下一个文件。

问题:

我们正在锁定网络服务器。每个文件只需要几分之一秒的时间来下载,大约 167 个文件后,网络服务器锁定(页面超时),存档过程暂停几分钟。然后存档过程又下载了大约 100 个文件,并再次停止了几分钟。这会持续几个小时,直到归档过程开始每分钟左右抓取一个文件。

看来 IIS6 的线程已用完,但我们如何才能阻止这种情况发生呢?

以下是我们正在运行的代码的精简版本。我已经删除了与我们的问题无关的日志记录和其他项目。有没有人有任何提示?

public class DownloadExample
{
    private WebClient _WebClient = new WebClient();

    public string DownloadDirectory { get; set; }
    public List<Report> ReportList { get; set; }

    /// <summary>
    /// Constructor - sets all the attributes needed to access the report server, download, and archive the reports
    /// </summary>
    /// <param name="userName">Username</param>
    /// <param name="userPassword">Password for the user's domain username</param>
    /// <param name="userDomain">Domain of the username</param>
    /// <param name="downloadDirectory">Network path where the files will be archived</param>
    public DownloadExample(string userName, string userPassword, string userDomain, string downloadDirectory, List<Report> reportList)
    {
        DownloadDirectory = downloadDirectory;
        _WebClient.Credentials = new NetworkCredential(userName, userPassword, userDomain);
        _WebClient.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(WebClient_DownloadFileCompleted);
        ReportList = reportList;
    }

    /// <summary>
    /// Kicks off the archive process
    /// </summary>
    public void StartDownloading()
    {
        if (ReportList.Count > 0)
        {
            Report rpt = ReportList[0];
            DoDownload(rpt.URL, CreateFileName(rpt), rpt.ReportTitle, rpt.ReportFormatType);
        }
    }

    /// <summary>
    /// Run the report and then download it to the archive directory
    /// </summary>
    /// <param name="url">URL of the Report</param>
    /// <param name="fileName">File name used to name the report file once it is downloaded</param>
    /// <param name="folderName">Name of the folder where the report will be downloaded to</param> 
    /// <param name="reportFormatType">Type of report being run, PDF or Excel</param>
    private bool DoDownload(string url, string fileName, string folderName, ReportFormatTypes reportFormatType)
    {
        bool isSuccess = false;

        string folderPath = DownloadDirectory + "\\" + folderName;
        DirectoryInfo dir = new DirectoryInfo(folderPath);
        if (!dir.Exists)
        {
            dir.Create();
            dir = null;
            dir = new DirectoryInfo(folderPath);
        }

        if (dir.Exists)
        {
            string path = folderPath + "\\" + fileName + ".xls";
            System.Uri uri = new Uri(url);
            try
            {
                _WebClient.DownloadFileAsync(uri, path);
            }
            catch (Exception exp)
            {
                //log error
            }
            FileInfo file = new FileInfo(path);
            isSuccess = file.Exists;
        }
        return isSuccess;
    }

    /// <summary>
    /// This event is fired after a file is downloaded
    /// After each file is downloaded, we remove the downloaded file from the list, 
    /// then download the next file.
    /// </summary>
    void WebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        //Remove the report that was just run
        ReportList.RemoveAt(0);
        if (ReportList.Count > 0)
        {
            //Download the next report
            Report rpt = ReportList[0];
            DoDownload(rpt.URL, CreateFileName(rpt), rpt.ReportTitle, rpt.ReportFormatType);
        }
    }

    /// <summary>
    /// Does a bunch of stuff to create the file name...
    /// </summary>
    string CreateFileName(Report rpt)
    {
        return rpt.FileName;
    }
}    
4

0 回答 0