Dostosowywanie limitu czasu połączenia HttpWebRequest w C#

wierzę, że po długich poszukiwaniach i poszukiwaniach odkryłem, że to, co chcę zrobić, jest prawdopodobnie lepiej obsługiwane przez skonfigurowanie połączenia asynchronicznego i zakończenie go po żądanym czasie... Ale i tak zapytam!

Szybki fragment kodu:

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.

Mam metodę HttpWebRequest, która znajduje się w aplikacji wielowątkowej, w której łączę się z dużą liczbą firmowych serwerów internetowych. W przypadku gdy serwer nie odpowiada, HttpWebRequest.GetResponse() trwa około 20 sekund do przerwy, mimo że określiłem limit czasu tylko 5 sekund. Aby uzyskać dostęp do serwerów w regularnych odstępach czasu, chcę pominąć te, które trwają dłużej niż 5 sekund, aby się połączyć.

Więc pytanie brzmi: " czy istnieje prosty sposób, aby określić / zmniejszyć limit czasu połączenia dla WebRequest lub HttpWebRequest?"

Author: SteveC, 2009-10-01

5 answers

I wierzę , że problem polega na tym, że WebRequest mierzy czas dopiero po złożeniu wniosku. Jeśli wyślesz wiele żądań na ten sam adres, to ServicePointManager ograniczy Twoje żądania i wyśle tylko tyle jednoczesnych połączeń, ile wynosi wartość odpowiadająca ServicePoint.ConnectionLimit który domyślnie pobiera wartość z ServicePointManager.DefaultConnectionLimit. Application CLR host ustawia to na 2, Asp host na 10. Więc jeśli masz wielowątkową aplikację, która przesyła wiele żądań do tego samego hosta tylko dwa są faktycznie umieszczone na przewodzie, reszta jest w kolejce.

Nie zbadałem tego do rozstrzygającego dowodu, czy to jest to, co naprawdę się dzieje, ale na podobnym projekcie miałem rzeczy były straszne, dopóki nie usunąłem ServicePoint ograniczenie.

Kolejnym czynnikiem, który należy wziąć pod uwagę, jest czas wyszukiwania DNS. Ponownie, czy moje przekonanie nie poparte twardymi dowodami, ale myślę, że WebRequest nie liczy czas wyszukiwania DNS z żądaniem przerwa. Czas wyszukiwania DNS może pojawić się jako bardzo duży czynnik czasu w niektórych wdrożeniach.

I tak, musisz zakodować swoją aplikację wokół WebRequest.BeginGetRequestStream (dla POST s z treścią) i WebRequest.BeginGetResponse (dla GET s i POSTSs). Wywołania synchroniczne nie będą skalowane(Nie będę wprowadzał szczegółów dlaczego, ale że nie mają twarde dowody). W każdym razie problem ServicePoint jest prostopadły do tego: zachowanie kolejkowania dzieje się również z wywołaniami asynchronicznymi.

 54
Author: Remus Rusanu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-01 02:41:24

Przepraszam, że taktuję Stary wątek, ale myślę, że coś, co zostało powiedziane powyżej, może być nieprawidłowe / wprowadzające w błąd.

Z tego co wiem .Timeout nie jest czasem połączenia, jest to całkowity czas dozwolony przez całe życie HttpWebRequest i odpowiedzi. Dowód:

I Set:

.Timeout=5000
.ReadWriteTimeout=32000

Czas połączenia i postu dla HttpWebRequest zajął 26ms

Ale kolejne wywołanie HttpWebRequest.GetResponse () timed out w 4974ms udowadniając tym samym, że 5000ms było limit czasu dla całego zestawu połączeń Wyślij zapytanie/uzyskaj odpowiedź.

Nie zweryfikowałem, czy rozdzielczość nazwy DNS została zmierzona jako część czasu, ponieważ nie jest to dla mnie nieistotne, ponieważ nic z tego nie działa tak, jak naprawdę potrzebuję go do działania-moim zamiarem było skrócenie czasu podczas łączenia się z systemami, które nie akceptują połączeń, jak pokazało ich awaria podczas fazy połączenia żądania.

Na przykład: jestem gotów czekać 30 sekund na żądanie połączenia, które ma szansa na zwrócenie wyniku, ale chcę tylko nagrać 10 sekund, czekając na wysłanie żądania do hosta, który źle się zachowuje.

 31
Author: TechSavvySam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-21 13:46:50

Coś, co znalazłem później, co pomogło, to .ReadWriteTimeout własność. To, oprócz właściwości .Timeout wydawało się w końcu skrócić czas, jaki wątki spędzają na próbach pobrania z problematycznego serwera. Domyślny czas dla .ReadWriteTimeout to 5 minut, co dla mojej aplikacji było zdecydowanie za długie.

Tak mi się wydaje:

.Timeout = Czas spędzony na próbie nawiązania połączenia (nie wliczając czasu wyszukiwania) .ReadWriteTimeout = Czas spędzony na próbie odczytu lub zapisu danych po połączeniu założona

Więcej informacji: HttpWebRequest.ReadWriteTimeout Property

Edit:

Na komentarz @KyleM, właściwość Timeout jest dla całej próby połączenia, a czytanie na niej w MSDN pokazuje:

Timeout to liczba milisekund, w których kolejne synchroniczne żądanie wykonane metodą GetResponse czeka na odpowiedź, a metoda GetRequestStream czeka na strumień. limit czasu dotyczy całego żądania i odpowiedzi, a nie indywidualnie na wywołania metody GetRequestStream i GetResponse. jeśli zasób nie zostanie zwrócony w okresie czasu, żądanie wyrzuci WebException z właściwością Status ustawioną na WebExceptionStatus.Przerwa.

(podkreślenie moje.)

 18
Author: JYelton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-02 15:57:16

Z dokumentacji HttpWebRequest.Właściwość Timeout:

Zapytanie o system nazw domen (DNS) może powrót trwa do 15 sekund lub przerwa. Jeśli twoja prośba zawiera Nazwa hosta, która wymaga rozdzielczości i Ustawiasz Timeout na wartość mniejszą niż 15 sekund, może to potrwać 15 sekund lub więcej przed wyrzuceniem WebException aby wskazać limit czasu na żądanie.

Czy to możliwe, że Twoje zapytanie DNS jest przyczyną timeoutu?

 13
Author: GBegen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-28 00:33:55

Bez względu na to, co próbowaliśmy, nie byliśmy w stanie uzyskać limitu czasu poniżej 21 sekund, gdy serwer, który sprawdzaliśmy, był wyłączony.

Aby obejść ten problem, połączyliśmy sprawdzenie TcpClient, aby sprawdzić, czy domena jest żywa, a następnie osobne sprawdzenie, czy adres URL jest aktywny

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;
}
 4
Author: Karl Glennon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-26 10:35:50