バックグラウンド:
毎年、特定の ASP.Net アプリケーションからのすべてのレポートを実行してアーカイブしています。アーカイブは、システムのデータが特定の時期にどのように見えるかの「スナップショット」を提供します。System.Net.WebClient を使用してレポート サーバーを呼び出し、レポートを特定のディレクトリにダウンロードする .Net Forms を使用して GUI を作成しました。これは過去にうまく機能しました。
今年は、アーカイブに Excel ファイルを含めます。Excel ファイルは、クエリ文字列を取り込んで Excel ファイルを返す .aspx ページ (Windows Sever 2003、IIS6、.Net 4.0) によって作成されます。これは 100 個程度の Excel ファイルではうまく機能しますが、その後問題が発生し始めます。毎年、約 300,000 ファイルをアーカイブしています。
力学:
UI スレッドをロックしないように、WebClient.DownloadFileAsync を使用してファイルをプルダウンします。各ファイルのダウンロードがいつ完了したかを伝えるために、WebClient.DownloadFileCompleted イベントに依存しています。DownloadFileCompleted が発生すると、次のファイルのダウンロードを開始します。
問題:
ウェブサーバーをロックしています。各ファイルのダウンロードにはほんの一瞬しかかからず、約 167 ファイルを超えると Web サーバーがロックされ (ページ タイムアウト)、アーカイブ プロセスが数分間一時停止します。その後、アーカイブ プロセスはさらに 100 個ほどのファイルをダウンロードし、数分間再び停止します。これは、アーカイブ プロセスが約 1 分ごとに 1 つのファイルでクロールを開始するまで、数時間続きます。
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;
}
}