私はデータベースからファイルコンテンツをストリーミングする代替手段を探していません、実際に問題の根本を探しています、これはファイルを実行していましたIIS 6ここでクラシックモードでアプリを実行しましたIIS= 7にアップグレードし、パイプラインモードでアプリプールを実行し、この問題が発生しました。
クライアントリクエストに大きなファイルを配信するハンドラーがあります。そして、私は次の問題に直面しています、
ファイルの平均サイズは4〜100 MBであるため、80 MBのファイルダウンロードの場合を考えてみましょう。
バッファリングオン、スロースタート
Response.BufferOutput = True;
これにより、ファイルの起動が非常に遅くなります。ユーザーのダウンロードやプログレスバーも数秒、通常は3〜20秒まで表示されないため、理由はIIS最初にファイル全体を読み取り、ファイルはビデオプレーヤーで再生されており、非常に低速で実行されますが、iPadはファイルの一部のみを最初にダウンロードするため、高速に動作します。
バッファリングオフ、コンテンツ長なし、ファストスタート、進捗なし
Reponse.BufferOutput = False;
これにより、すぐに開始されますが、エンドクライアント(Chromeなどの一般的なブラウザー)はContent-LengthをIISも知らないため、進行状況を表示せず、代わりにX KBがダウンロードされたと表示します。
バッファリングオフ、手動コンテンツ長、ファストスタート、進行状況、プロトコル違反
Response.BufferOutput = False;
Response.AddHeader("Content-Length", file.Length);
これにより、Chromeなどでの正しい即時ファイルダウンロードが行われますが、場合によってはIISハンドラーは「リモートクライアントの接続を閉じました」エラーになります(これは非常に頻繁です) これは、すべてのリクエストではなく、すべてのリクエストの5〜10%で発生します。
何が起こっているのでしょうか?IISはバッファリングをしないと100クライアントと呼ばれるものを送信せず、クライアントは出力を期待せずに切断する可能性があります。ただし、ソースからのファイルの読み込みには時間がかかりますが、クライアント側でタイムアウトを増やしましたが、IIS timesoutのようで、制御できません。
とにかくResponseに100を送信させ、だれにも接続を閉じさせないように強制することができますか
[〜#〜] update [〜#〜]
Firefox/Chromeで次のヘッダーを見つけましたが、ここではプロトコル違反または不良ヘッダーについては何も異常ではないようです。
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:1728000
Cache-Control:private
Content-Disposition:attachment; filename="24.jpg"
Content-Length:22355
Content-Type:image/pjpeg
Date:Wed, 07 Mar 2012 13:40:26 GMT
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
更新2
リサイクルを回してもまだ多くは提供されませんでしたが、MaxWorkerProcessを8に増やしたので、以前よりもエラーの数が少なくなりました。
しかし、平均して、1秒間に200リクエストのうち、2〜10リクエストが失敗します。これは、ほぼ1秒おきに発生します。
更新
「サーバーがプロトコル違反をコミットしました。セクション= ResponseStatusLine」で失敗するリクエストの5%を続けて、WebClientを使用するWebサーバーからコンテンツをダウンロードする別のプログラムがあります。リクエストの5%が失敗します。とにかくWebClientの障害をトレースする方法はありますか?
問題の再定義
受信したゼロバイトファイル
IISは何らかの理由で接続を閉じます。WebConfigのクライアント側で、0バイトではないファイルに対して0バイトを受け取ります。SHA1ハッシュチェックを行い、IIS Webサーバー、エラーは記録されません。
これは私の間違いであり、Entity Frameworkを使用することで解決し、読み取りがトランザクションスコープにないためダーティ(コミットされていない行)を読み取り、トランザクションスコープに入れることでこの問題は解決しました。
プロトコル違反例外が発生
WebClientは、「サーバーがプロトコル違反をコミットしました。セクション= ResponseStatusLine。」と言ってWebExceptionをスローします。
安全でないヘッダー解析を有効にできることはわかっていますが、それがポイントではありません。適切なヘッダーを送信しているのがHTTPハンドラーである場合、なぜIIS 、異常なことは何もありません)、これはたった2%の頻度で発生します。
更新4
Sc-win32 64エラーが見つかり、MinBytesPerSecondのWebLimitsを240から0に変更する必要があることをどこかで読みましたが、それでもすべて同じです。ただし、IIS 64 sc-win32エラーをログに記録するたびに、IISはHTTPステータスを200として記録しますが、何らかのエラーが発生しました。大量のファイルが生成されるため、200のトレースロギング。
上記の問題は両方ともMinBytesPerSecondを増やすことで解決され、セッションを無効にするだけでなく、すべてのポイントを要約した詳細な回答を追加しました。
IISで大きなファイルを配信する正しい方法は次のオプションですが、
BufferOutputでコンテンツの長さをfalseに設定した場合、失敗の考えられる理由は、IIS送信するファイルをgzipしようとし、Content-Length IISは圧縮されたものに戻すことができず、エラーが始まります(*)。
そう keep the BufferOutput to false, and second disable the gzip from iis for the files you send
-または、すべてのファイルのiis gzipを無効にして、gzip部分をプログラムで処理し、送信するファイルをgzipから除外します。
同じ理由でいくつかの同様の質問: ASP.NETサイトは時々フリーズしたり、ロード中にページの上部に奇妙なテキストを表示したり、負荷分散されたサーバーで
HTTP圧縮:一部の外部スクリプト/ CSSが時々適切に解凍しない
(*)もう一度変更しないのはなぜですか? IISでこのオプションを有効にし、ヘッダーがすべてブラウザに送信される準備が整っていない場合を除いて、ヘッダーを設定した瞬間から参加することはできません。
Gzipされていない場合、次に思い浮かぶのは、ファイルが送信され、何らかの理由で接続が遅延し、タイムアウトして閉じられたことです。そのため、「リモートホストは接続を閉じました」を取得します。
これは原因に応じて解決できます。
IISからの場合は、Webサイトのプロパティに移動して、最大の「接続タイムアウト」と「HTTPキープアライブを有効にする」を設定してください。
Web.configを変更することによるページタイムアウト(1つの特定のページに対してのみプログラムで変更できます)
<httpRuntime executionTimeout="43200"
以下もご覧ください: http://weblogs.asp.net/aghausman/archive/2009/02/20/prevent-request-timeout-in-asp-net.aspx
もう1つ確認する必要があるのは、ファイルを送信するために使用するハンドラーでセッションを使用しないことです。セッションが完了するまでアクションをロックし、ユーザーがファイルをダウンロードするのにより長い時間がかかると、2番目のタイムアウト。
いくつかの相対的な:
あまり知られていないASP.NET Response.TransmitFile メソッドを使用します。これは非常に高速で(そしておそらくIISカーネルキャッシュ)を使用し、 Windowsのアンマネージド TransmitFile APIに基づいています。
ただし、このAPIを使用するには、転送する物理ファイルが必要です。架空のmyCacheFilePath物理ファイルパスを使用してこれを行う方法を説明する擬似c#コードを次に示します。クライアントキャッシングの可能性もサポートします。もちろん、既に手元にファイルがある場合は、そのキャッシュを作成する必要はありません。
if (!File.Exists(myCacheFilePath))
{
LoadMyCache(...); // saves the file to disk. don't do this if your source is already a physical file (not stored in a db for example).
}
// we suppose user-agent (browser) cache is enabled
// check appropriate If-Modified-Since header
DateTime ifModifiedSince = DateTime.MaxValue;
string ifm = context.Request.Headers["If-Modified-Since"];
if (!string.IsNullOrEmpty(ifm))
{
try
{
ifModifiedSince = DateTime.Parse(ifm, DateTimeFormatInfo.InvariantInfo);
}
catch
{
// do nothing
}
// file has not changed, just send this information but truncate milliseconds
if (ifModifiedSince == TruncateMilliseconds(File.GetLastWriteTime(myCacheFilePath)))
{
ResponseWriteNotModified(...); // HTTP 304
return;
}
}
Response.ContentType = contentType; // set your file content type here
Response.AddHeader("Last-Modified", File.GetLastWriteTimeUtc(myCacheFilePath).ToString("r", DateTimeFormatInfo.InvariantInfo)); // tell the client to cache that file
// this API uses windows lower levels directly and is not memory/cpu intensive on Windows platform to send one file. It also caches files in the kernel.
Response.TransmitFile(myCacheFilePath)
このコードは、私にとってはうまくいきます。クライアントへのデータストリームをすぐに開始します。ダウンロード中に進行状況が表示されます。 HTTPに違反しません。 Content-Lengthヘッダーが指定され、チャンク転送エンコードは使用されません。
protected void PrepareResponseStream(string clientFileName, HttpContext context, long sourceStreamLength)
{
context.Response.ClearHeaders();
context.Response.Clear();
context.Response.ContentType = "application/pdf";
context.Response.AddHeader("Content-Disposition", string.Format("filename=\"{0}\"", clientFileName));
//set cachebility to private to allow IE to download it via HTTPS. Otherwise it might refuse it
//see reason for HttpCacheability.Private at http://support.Microsoft.com/kb/812935
context.Response.Cache.SetCacheability(HttpCacheability.Private);
context.Response.Buffer = false;
context.Response.BufferOutput = false;
context.Response.AddHeader("Content-Length", sourceStreamLength.ToString (System.Globalization.CultureInfo.InvariantCulture));
}
protected void WriteDataToOutputStream(Stream sourceStream, long sourceStreamLength, string clientFileName, HttpContext context)
{
PrepareResponseStream(clientFileName, context, sourceStreamLength);
const int BlockSize = 4 * 1024 * 1024;
byte[] buffer = new byte[BlockSize];
int bytesRead;
Stream outStream = m_Context.Response.OutputStream;
while ((bytesRead = sourceStream.Read(buffer, 0, BlockSize)) > 0)
{
outStream.Write(buffer, 0, bytesRead);
}
outStream.Flush();
}