web-dev-qa-db-ja.com

C#でのHttpWebRequest接続タイムアウトの調整

長い調査と検索の結果、非同期接続を設定し、希望するタイムアウト後に終了することで、おそらくやりたいことの方が役立つことを発見したと思います...しかし、私は行きますとにかく聞いてください!

コードの簡単なスニペット:

_HttpWebRequest webReq = (HttpWebRequest)HttpWebRequest.Create(url);
webReq.Timeout = 5000;
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse(); 
// this takes ~20+ sec on servers that aren't on the proper port, etc.
_

マルチスレッドアプリケーションにあるHttpWebRequestメソッドがあります。このメソッドでは、多数の会社のWebサーバーに接続しています。サーバーが応答しない場合、5秒のタイムアウトを指定したにもかかわらず、HttpWebRequest.GetResponse()がタイムアウトするまでに約20秒かかります。定期的にサーバーを通過するために、接続に5秒以上かかるサーバーをスキップしたいと思います。

質問は次のとおりです。 "WebRequestまたはHttpWebRequestの接続タイムアウトを指定/減少する簡単な方法はありますか?"

45
JYelton

Ibelieve問題は、WebRequestが要求を実際に行った後にのみ時間を測定することです。同じアドレスに複数のリクエストを送信すると、ServicePointManagerはリクエストを抑制し、対応する ServicePoint.ConnectionLimit デフォルトでは ServicePointManager.DefaultConnectionLimit 。アプリケーションCLRホストはこれを2に設定します。ASP Host to10。したがって、同じホストに複数の要求を送信するマルチスレッドアプリケーションがある場合、実際には2つだけがワイヤに配置され、残りはキューに入れられますアップ。

これが本当に起こるかどうかを決定的な証拠としてこれを調査したことはありませんが、同様のプロジェクトでは、ServicePointの制限を取り除くまで物事は恐ろしいものでした。

考慮すべきもう1つの要因は、DNSルックアップ時間です。繰り返しますが、私の信念は確固たる証拠に裏付けられていませんが、WebRequestnotリクエストタイムアウトに対してDNSルックアップ時間をカウントすると思います。 DNSルックアップ時間は、一部の展開では非常に大きな時間要因として表示される場合があります。

はい、アプリを WebRequest.BeginGetRequestStream (コンテンツを含むPOSTsの場合)および WebRequest.BeginGetResponseGETsの場合andPOSTSs)。同期呼び出しはスケーリングされません(詳細については説明しませんが、その理由doには確固たる証拠があります)。とにかく、ServicePointの問題はこれに直行します。キューイング動作は非同期呼び出しでも発生します。

57
Remus Rusanu

古いスレッドにタックしてすみませんが、上記のことは間違っている/誤解を招く可能性があると思います。

私が知ることができることから、タイムアウトは接続時間ではなく、HttpWebRequestと応答の寿命全体に許可されている合計時間です。証明:

設定します:

.Timeout=5000
.ReadWriteTimeout=32000

HttpWebRequestの接続およびポスト時間は26ミリ秒かかりました

しかし、その後のHttpWebRequest.GetResponse()の呼び出しは4974msでタイムアウトしたため、5000msが送信要求/応答の取得の呼び出しセット全体の時間制限であることを証明しました。

DNSの名前解決が時間の一部として測定されたかどうかは確認しませんでした。これは、これが実際に必要な方法で機能しないためです。要求の接続フェーズ中に失敗したことからわかるように、接続を受け入れていませんでした。

たとえば、結果を返す可能性のある接続要求で30秒待機しますが、不正な動作をしているホストに要求を送信するのを待機しているのは10秒だけです。

35
TechSavvySam

後で助けになったものは、.ReadWriteTimeoutプロパティです。これは、.Timeoutプロパティに加えて、スレッドが問題のあるサーバーからのダウンロードに費やす時間を最終的に削減したようです。 .ReadWriteTimeoutのデフォルトの時間は5分で、これは私のアプリケーションでは長すぎます。

だから、それは私には思えます:

.Timeout =接続の確立の試行に費やした時間(ルックアップ時間を除く).ReadWriteTimeout =接続の確立後のデータの読み取りまたは書き込みの試行に費やした時間

詳細: HttpWebRequest.ReadWriteTimeoutプロパティ

編集:

@KyleMのコメントによると、Timeoutプロパティは接続試行全体に対するものであり、MSDNでそれを読んでいます:

タイムアウトは、GetResponseメソッドを使用して行われた後続の同期要求が応答を待機し、GetRequestStreamメソッドがストリームを待機するミリ秒数です。 タイムアウトは、GetRequestStreamおよびGetResponseメソッド呼び出しに個別にではなく、リクエストとレスポンス全体に適用されます。タイムアウト期間内にリソースが返されない場合、リクエストは、StatusプロパティをWebExceptionStatus.Timeoutに設定してWebExceptionをスローします。

(エンファシス鉱山。)

19
JYelton

HttpWebRequest.Timeoutプロパティのドキュメントから:

ドメインネームシステム(DNS)クエリが返されるか、タイムアウトになるまでに最大15秒かかる場合があります。リクエストに解決が必要なホスト名が含まれ、タイムアウトを15秒未満の値に設定した場合、リクエストのタイムアウトを示すためにWebExceptionがスローされるまでに15秒以上かかる場合があります。

DNSクエリがタイムアウトの原因である可能性はありますか?

14
GBegen

私たちが何を試みたとしても、チェックしているサーバーがダウンしたときに21秒未満のタイムアウトを得ることができませんでした。

これを回避するために、TcpClientチェックを組み合わせてドメインが生きているかどうかを確認し、続いてURLがアクティブかどうかを確認する別のチェックを組み合わせました

public static bool IsUrlAlive(string aUrl, int aTimeoutSeconds)
{
    try
    {
        //check the domain first
        if (IsDomainAlive(new Uri(aUrl).Host, aTimeoutSeconds))
        {
            //only now check the url itself
            var request = System.Net.WebRequest.Create(aUrl);
            request.Method = "HEAD";
            request.Timeout = aTimeoutSeconds * 1000;
            var response = (HttpWebResponse)request.GetResponse();
            return response.StatusCode == HttpStatusCode.OK;
        }
    }
    catch
    {
    }
    return false;

}

private static bool IsDomainAlive(string aDomain, int aTimeoutSeconds)
{
    try
    {
        using (TcpClient client = new TcpClient())
        {
            var result = client.BeginConnect(aDomain, 80, null, null);

            var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(aTimeoutSeconds));

            if (!success)
            {
                return false;
            }

            // we have connected
            client.EndConnect(result);
            return true;
        }
    }
    catch
    {
    }
    return false;
}
8
Karl Glennon