Szczegółowe badanie wyjątku limitu czasu WCF

94

Mamy aplikację, która ma usługę WCF (* .svc) działającą w usługach IIS7 i różnych klientów wysyłających zapytania do usługi. Na serwerze działa Win 2008 Server. Klienci korzystają z systemu Windows 2008 Server lub Windows 2003 Server. Otrzymuję następujący wyjątek, który, jak widziałem, może być w rzeczywistości powiązany z dużą liczbą potencjalnych problemów z WCF.

System.TimeoutException: The request channel timed out while waiting for a reply after 00:00:59.9320000. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. ---> System.TimeoutException: The HTTP request to 'http://www.domain.com/WebServices/myservice.svc/gzip' has exceeded the allotted timeout of 00:01:00. The time allotted to this operation may have been a portion of a longer timeout. 

Zwiększyłem limit czasu do 30 minut, a błąd nadal występował. To mówi mi, że dzieje się coś innego, ponieważ przesłanie lub pobranie takiej ilości danych nigdy nie zajęłoby 30 minut.

Błąd pojawia się i znika. W tej chwili jest to częstsze. Wydaje się, że nie ma znaczenia, czy mam 3 klientów działających jednocześnie, czy 100, to nadal występuje od czasu do czasu. Przez większość czasu nie ma limitów czasu, ale wciąż dostaję kilka na godzinę. Błąd pochodzi z dowolnej wywoływanej metody. Jedna z tych metod nie ma parametrów i zwraca trochę danych. Inny pobiera wiele danych jako parametr, ale jest wykonywany asynchronicznie. Błędy zawsze pochodzą od klienta i nigdy nie odwołują się do żadnego kodu na serwerze w śladzie stosu. Zawsze kończy się:

 at System.Net.HttpWebRequest.GetResponse()
  at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)

Na serwerze: próbowałem (i obecnie mam) następujące ustawienia powiązań:

maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647"

Wydaje się, że nie ma to wpływu.

Próbowałem (i obecnie mam) następujące ustawienia ograniczania przepustowości:

<serviceThrottling maxConcurrentCalls="1500"   maxConcurrentInstances="1500"    maxConcurrentSessions="1500"/>

Wydaje się, że nie ma to wpływu.

Obecnie mam następujące ustawienia usługi WCF.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]

Biegałem ConcurrencyMode.Multipleprzez chwilę, a błąd nadal występował.

Próbowałem ponownie uruchomić IIS, zrestartować mój bazowy SQL Server, zrestartować maszynę. Wszystko to wydaje się nie mieć wpływu.

Próbowałem wyłączyć zaporę systemu Windows. Wydaje się, że nie ma to wpływu.

Na kliencie mam takie ustawienia:

maxReceivedMessageSize="2147483647"

<system.net>
    <connectionManagement>
    <add address="*" maxconnection="16"/>
</connectionManagement> 
</system.net>

Mój klient zamyka swoje połączenia:

var client = new MyClient();

try
{
    return client.GetConfigurationOptions();
}
finally
{
    client.Close();
}

Zmieniłem ustawienia rejestru, aby umożliwić więcej połączeń wychodzących:

MaxConnectionsPerServer=24, MaxConnectionsPer1_0Server=32.

Niedawno wypróbowałem SvcTraceViewer.exe. Udało mi się złapać jeden wyjątek po stronie klienta. Widzę, że trwa 1 minutę. Patrząc na ślad po stronie serwera, widzę, że serwer nie jest świadomy tego wyjątku. Maksymalny czas trwania, jaki widzę, to 10 sekund.

Przyjrzałem się aktywnym połączeniom z bazą danych używającym exec sp_whona serwerze. Mam tylko kilka (2-3). Przyjrzałem się połączeniom TCP od jednego klienta za pomocą TCPview. Zwykle jest to około 2-3, a widziałem do 5 lub 6.

Mówiąc najprościej, jestem zaskoczony. Próbowałem wszystkiego, co mogłem znaleźć, i chyba brakuje mi czegoś bardzo prostego, co mógłby zobaczyć ekspert WCF. Mam przeczucie, że coś blokuje moich klientów na niskim poziomie (TCP), zanim serwer faktycznie otrzyma wiadomość i / lub że coś ustawia wiadomości w kolejce na poziomie serwera i nigdy nie pozwala im przetwarzać.

Jeśli masz jakieś liczniki wydajności, którym powinienem się przyjrzeć, daj mi znać. (proszę wskazać, które wartości są złe, ponieważ niektóre z tych liczników są trudne do rozszyfrowania). Ponadto, jak mogę zarejestrować rozmiar komunikatu WCF? Wreszcie, czy są tam jakieś narzędzia, które pozwoliłyby mi przetestować, ile połączeń mogę nawiązać między moim klientem a serwerem (niezależnie od mojej aplikacji)

Dziękuję za Twój czas!

Dodatkowe informacje dodane 20 czerwca:

Moja aplikacja WCF robi coś podobnego do następującego.

while (true)
{
   Step1GetConfigurationSettingsFromServerViaWCF(); // can change between calls
   Step2GetWorkUnitFromServerViaWCF();
   DoWorkLocally(); // takes 5-15minutes. 
   Step3SendBackResultsToServerViaWCF();
}

Korzystając z WireShark, zauważyłem, że gdy wystąpi błąd, mam pięć retransmisji TCP, a następnie resetowanie TCP później. Domyślam się, że RST pochodzi z WCF zabijającego połączenie. Raport wyjątków, który otrzymuję, pochodzi z przekroczenia limitu czasu kroku 3.

Odkryłem to, patrząc na strumień tcp „tcp.stream eq 192”. Następnie rozszerzyłem mój filtr do „tcp.stream eq 192 i http i http.request.method eq POST” i zobaczyłem 6 wpisów POST podczas tego strumienia. Wydawało się to dziwne, więc sprawdziłem z innym strumieniem, takim jak tcp.stream eq 100. Miałem trzy posty POST, co wydaje się nieco bardziej normalne, ponieważ wykonuję trzy wywołania. Jednak zamykam połączenie po każdym wywołaniu WCF, więc spodziewałbym się jednego wywołania na strumień (ale nie wiem zbyt wiele o TCP).

Badając nieco więcej, zrzuciłem ładowanie pakietu HTTP na dysk, aby sprawdzić, gdzie te sześć wywołań.

1) Step3
2) Step1
3) Step2
4) Step3 - corrupted
5) Step1
6) Step2

Domyślam się, że dwóch równoczesnych klientów używa tego samego połączenia, dlatego widziałem duplikaty. Jednak wciąż mam kilka innych problemów, których nie mogę zrozumieć:

a) Dlaczego pakiet jest uszkodzony? Losowy błąd sieciowy - może? Ładunek jest zgzipowany przy użyciu tego przykładowego kodu: http://msdn.microsoft.com/en-us/library/ms751458.aspx - Czy kod może czasami zawierać błędy, jeśli jest używany jednocześnie? Powinienem testować bez biblioteki gzip.

b) Dlaczego miałbym zobaczyć kroki 1 i 2 uruchomione PO przekroczeniu limitu czasu uszkodzonej operacji? Wydaje mi się, że te operacje nie powinny były mieć miejsca. Może nie patrzę na właściwy strumień, ponieważ moje rozumienie TCP jest błędne. Mam inne strumienie, które pojawiają się w tym samym czasie. Powinienem zbadać inne strumienie - szybki rzut oka na strumienie 190-194 pokazują, że Step3 POST ma prawidłowe dane ładunku (nie są uszkodzone). Popycha mnie, bym ponownie spojrzał na bibliotekę gzip.

Jason Kealey
źródło
Jason - czy kiedykolwiek rozwiązałeś ten problem? Czy było to ustawienie DefaultConnectionLimit?
SFun28
2
@JasonKealey - W przeciwieństwie do wielu innych pytań, nie można zarzucić, że nie próbowałeś samemu zadać pytania :) Podoba mi się, że Twoje pytanie jest tak szczegółowe i zawiera wszystkie ważne szczegóły. Objawy, które opisujesz, są bardzo podobne do moich, więc mam nadzieję, że rozwiązanie jest takie samo :)
Øyvind Bråthen

Odpowiedzi:

51

Jeśli używasz klienta .Net, być może nie ustawiłeś

//This says how many outgoing connection you can make to a single endpoint. Default Value is 2
System.Net.ServicePointManager.DefaultConnectionLimit = 200;

Oto oryginalne pytanie i odpowiedź Ograniczanie usługi WCF

Aktualizacja :

Ta konfiguracja jest umieszczana w aplikacji klienta .Net może być podczas uruchamiania lub kiedykolwiek, ale przed rozpoczęciem testów.

Ponadto możesz go mieć w pliku app.config, a także w następujący sposób

<system.net>
    <connectionManagement>
      <add maxconnection = "200" address ="*" />
    </connectionManagement>
  </system.net>
Mubaszar
źródło
To wygląda obiecująco. Dodałem to do przetestowania podczas mojego następnego testu skalowalności. Wygląda dokładnie tak, jak przypadkowe ustawienie, które spowodowałoby awarię :) Dzięki za wskaźnik.
Jason Kealey
1
@Jason: Jeśli jesteś programistą serwerów, wiesz, jak ważne jest utrzymanie skalowalności serwera w Twoich rękach, a także taki, który obecnie boryka się z problemem współbieżności, nawet po użyciu powyższego. Proszę, jeśli możesz spojrzeć na następujące pytanie stackoverflow.com/questions/2637175/wcf-network-cost, w skrócie cierpię na opóźnienie 31 ms między klientem a serwerem i muszę je zmniejszyć.
Mubashar
3
Zajęło to tylko rok, ale w końcu przeprowadziłem kolejny test warunków skrajnych w aplikacji z ustawioną flagą. Wydaje się, że problem został rozwiązany, więc dam ci najlepszą odpowiedź. Nie zdziwiłbym się, że był to ostatni element układanki, który był wymagany, ale wszystkie pozostałe elementy musiały być na miejscu, aby błąd się nie pojawił. Dziękuję bardzo!
Jason Kealey,
2
@ Aris: W aplikacji klienta .net, podczas uruchamiania lub gdziekolwiek ustawisz konfigurację globalną, jeśli chcesz zachować konfigurowalność, możesz dodać ją w pliku konfiguracyjnym, jak również w tym <system.net> <connectionManagement> <add maxconnection = "200" address = "*" /> </connectionManagement> </system.net>
Mubashar
3

Jeśli jeszcze tego nie próbowałeś - hermetyzuj operacje WCF po stronie serwera w blokach try / Final i Dodaj rejestrowanie, aby upewnić się, że faktycznie zwracają.

Jeśli pokażą, że operacje się kończą, następnym krokiem byłoby zejście na niższy poziom i przyjrzenie się rzeczywistej warstwie transportowej.

W tym momencie bardzo pomocny może być program Wireshark lub inne podobne narzędzie do przechwytywania pakietów. Zakładam, że działa to przez HTTP na standardowym porcie 80.

Uruchom Wireshark na kliencie. W Opcjach po rozpoczęciu przechwytywania ustaw filtr przechwytywania natcp http and host service.example.com - zmniejszy to ilość nieistotnego ruchu.

Jeśli możesz, zmodyfikuj swojego klienta, aby powiadomił Cię o dokładnym czasie rozpoczęcia połączenia oraz o czasie, kiedy upłynął limit czasu. Albo po prostu uważnie to obserwuj.

Gdy pojawi się błąd, możesz przeszukać dzienniki Wireshark, aby znaleźć początek połączenia. Kliknij prawym przyciskiem myszy pierwszy pakiet, który wywołuje Twój klient (powinno to być coś w rodzaju GET /service.svc lub POST /service.svc) i wybierz Śledź strumień TCP.

Wireshark zdekoduje całą konwersację HTTP, dzięki czemu możesz upewnić się, że WCF faktycznie odsyła odpowiedzi.


źródło
Mam logowanie na serwerze - na tym końcu nie ma błędu. Uruchamiam teraz WireSharka, aby zobaczyć, co mogę znaleźć. Biorąc pod uwagę duże natężenie ruchu, analiza będzie trudna, ale poinformuję o tym, jeśli znajdę cokolwiek.
Jason Kealey
Uruchomiłem WireShark w ciągu ostatnich sześciu godzin i zebrałem około 60 000 klatek. Ten klient zgłosił dziś tylko jeden wyjątek. Widziałem połączenie TCP oznaczone jako RST (reset), najwyraźniej po wysłaniu e-maila z błędem, który prawdopodobnie jest zakończeniem połączenia WCF. Zapisałem ładunek (525k) na dysku. Sprawdziłem, że było 87 innych wywołań z ładunkami o podobnej wielkości. Widziałem kilka retransmisji TCP, ale widziałem również niektóre w innych wywołaniach (to się nie udało). Zaczynam się zastanawiać nad moim sprzętem sieciowym i kablami.
Jason Kealey
Nawet w sieci lokalnej obecność retransmisji TCP niekoniecznie jest zła. Jeśli można fizycznie podłączyć dwa punkty końcowe do jednego przełącznika, to może być warte spróbowania, ale nie miałbym nadziei, że to naprawi. Jeśli możesz - utwórz bardzo podstawową aplikację kliencką, która po prostu przekazuje część ruchu do serwera i z powrotem, i nic więcej. Może to pomóc w wyeliminowaniu wszelkich problemów w aplikacji, które mogą powodować przekroczenia limitów czasu.
Wspomniałeś również o zobaczeniu pakietu resetowania TCP - czy serwer dostarczył w tym momencie jakąś odpowiedź (czy może czekał na więcej danych)? Czy między RST a poprzednim pakietem było znaczne opóźnienie?
Serwer jest zdalny. Planuję utworzyć lokalnie środowisko testowe, aby sprawdzić, czy to pomoże. Jeśli chodzi o RST, został wysłany 34 sekundy po ostatniej z pięciu retransmisji TCP. (1 do 8 sekund przerwy między retransmisjami). Czy to daje jakieś wskazówki?
Jason Kealey
2

od: http://www.codeproject.com/KB/WCF/WCF_Operation_Timeout_.aspx

Aby uniknąć tego błędu limitu czasu, musimy skonfigurować właściwość OperationTimeout dla serwera proxy w kodzie klienta WCF. Ta konfiguracja jest czymś nowym w przeciwieństwie do innych konfiguracji, takich jak Limit czasu wysyłania, Limit czasu odbioru itp., Które omówiłem na początku artykułu. Aby ustawić tę konfigurację właściwości limitu czasu operacji, musimy rzutować nasz serwer proxy na IContextChannel w aplikacji klienta WCF przed wywołaniem metod kontraktu operacji.

Joel Martinez
źródło
Próbowałem tego. Niezależnie od ustawionego limitu czasu, nadal upływa limit czasu, ale nie ma to sensu, ponieważ operacja nie trwa tak długo, a wszyscy inni klienci wykonujący te same zapytania działają w tym czasie.
Jason Kealey
Moje testy wykazały, że OperationTimeout po prostu zastępuje ReceiveTimeout z pliku config. Tak więc nie ma to żadnego sensu.
dudeNumber4
2

Mam bardzo podobny problem. W przeszłości było to związane z problemami z serializacją. Jeśli nadal masz ten problem, czy możesz sprawdzić, czy możesz poprawnie serializować zwracane obiekty. W szczególności, jeśli używasz obiektów Linq-To-Sql, które mają relacje, istnieją znane problemy z serializacją, jeśli umieścisz odwołanie wsteczne na obiekcie podrzędnym w obiekcie nadrzędnym i oznaczysz to odwołanie wsteczne jako DataMember.

Możesz zweryfikować serializację, pisząc aplikację konsolową, która serializuje i deserializuje obiekty przy użyciu DataContractSerializer po stronie serwera i wszelkich metod serializacji używanych przez klienta. Na przykład w naszej obecnej aplikacji mamy klientów zarówno WPF, jak i Compact Framework. Napisałem aplikację konsolową, aby sprawdzić, czy mogę serializować za pomocą DataContractSerializer i deserializować za pomocą XmlDesserializer. Możesz tego spróbować.

Ponadto, jeśli zwracasz obiekty Linq-To-Sql, które mają kolekcje podrzędne, możesz spróbować upewnić się, że chętnie ładowałeś je po stronie serwera. Czasami z powodu leniwego ładowania zwracane obiekty nie są zapełniane i mogą powodować zachowanie, które widzisz, gdy żądanie jest wysyłane do metody usługi wiele razy.

Jeśli rozwiązałeś ten problem, chciałbym usłyszeć, jak to zrobić, ponieważ ja też z tym utknąłem. Po sprawdzeniu, że mój problem nie dotyczy serializacji, jestem zagubiony.

AKTUALIZACJA: Nie jestem pewien, czy to ci pomoże, ale narzędzie Service Trace Viewer właśnie rozwiązało mój problem po 5 dniach bardzo podobnego doświadczenia do twojego. Konfigurując śledzenie, a następnie przeglądając nieprzetworzony kod XML, znalazłem wyjątki, które powodowały problemy z serializacją. Był powiązany z obiektami Linq-to-SQL, które czasami miały więcej obiektów podrzędnych, niż można było z powodzeniem serializować. Dodanie następujących elementów do pliku web.config powinno umożliwić śledzenie:

<sharedListeners>
    <add name="sharedListener"
         type="System.Diagnostics.XmlWriterTraceListener"
         initializeData="c:\Temp\servicetrace.svclog" />
  </sharedListeners>
  <sources>
    <source name="System.ServiceModel" switchValue="Verbose, ActivityTracing" >
      <listeners>
        <add name="sharedListener" />
      </listeners>
    </source>
    <source name="System.ServiceModel.MessageLogging" switchValue="Verbose">
      <listeners>
        <add name="sharedListener" />
      </listeners>
    </source>
  </sources>

Wynikowy plik można otworzyć za pomocą narzędzia Service Trace Viewer lub po prostu w przeglądarce IE, aby sprawdzić wyniki.

Brett Bim
źródło
2

Czy zamykasz połączenie z usługą WCF między żądaniami? Jeśli tego nie zrobisz, zobaczysz dokładny limit czasu (ostatecznie).

aridlehoover
źródło
2

Właśnie rozwiązałem problem, stwierdziłem, że węzły w pliku App.config są źle skonfigurowane.

<client>
<endpoint name="WCF_QtrwiseSalesService" binding="wsHttpBinding" bindingConfiguration="ws" address="http://cntgbs1131:9005/MyService/TGE.ISupplierClientManager" contract="*">
</endpoint>
</client>

<bindings>
    <wsHttpBinding>
        <binding name="ws" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text">
            <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
            <**security mode="None">**
                <transport clientCredentialType="None"></transport>
            </security>
        </binding>
    </wsHttpBinding>
</bindings>

Potwierdź konfigurację w węźle <security>, wartość atrybutu „tryb” to „Brak”. Jeśli wartością jest „Transport”, wystąpi błąd.

alexanderlc
źródło
Czy to nie wpływa na bezpieczeństwo? Jeśli tak, to może nie być rozwiązaniem dla większości rzeczywistych zastosowań
Veverke
0

Czy próbowałeś użyć clientVia, aby zobaczyć wysłaną wiadomość, używając zestawu narzędzi SOAP lub czegoś podobnego? Może to pomóc sprawdzić, czy błąd pochodzi od samego klienta, czy z innego miejsca.

Philippe
źródło
Czy znasz jakieś narzędzia nowsze niż przestarzały zestaw narzędzi SOAP, które ułatwiłyby mi rejestrowanie tych informacji w wywołaniach WCF?
Jason Kealey
SOAP Toolkit todeprecated
Kiquenet,
0

Czy sprawdziłeś ślady WCF? WCF ma tendencję do połykania wyjątków i zwracania tylko ostatniego wyjątku, czyli limitu czasu, który otrzymujesz, ponieważ punkt końcowy nie zwrócił nic sensownego.

Miki Watts
źródło
Wypróbowałem SvcTraceViewer i jedynym zgłoszonym przez niego wyjątkiem był limit czasu (na kliencie). Nic nie zostało zgłoszone na serwerze.
Jason Kealey
Otwórz wszystkie opcje śledzenia, możesz nie mieć otwartych wszystkich opcji śledzenia. Sprawdź również pliki śledzenia zdarzeń i śledzenia komunikatów.
Miki Watts
0

Ten błąd zostanie również wyświetlony, jeśli przekazujesz klientowi obiekt, który zawiera właściwość typu enum, która nie jest ustawiona domyślnie, a wyliczenie nie ma wartości odwzorowanej na 0. tj. enum MyEnum{ a=1, b=2};

tim
źródło
0

Wygląda na to, że ten komunikat o wyjątku jest dość ogólny i może zostać odebrany z różnych powodów. Napotkaliśmy to podczas wdrażania klienta na komputerach z systemem Windows 8.1. Nasz klient WCF działa w usłudze systemu Windows i stale sonduje usługę WCF. Usługa systemu Windows działa na koncie użytkownika innego niż administrator. Problem został rozwiązany przez ustawienie clientCredentialType na „Windows” w konfiguracji WCF, aby umożliwić uwierzytelnianie do przekazywania, jak w następującym:

      <security mode="None">
        <transport clientCredentialType="Windows" proxyCredentialType="None"
          realm="" />
        <message clientCredentialType="UserName" algorithmSuite="Default" />
      </security>
Alexander Liberson
źródło
0

Nie jestem ekspertem od WCF, ale zastanawiam się, czy nie korzystasz z ochrony DDOS w usługach IIS. Z doświadczenia wiem, że jeśli uruchomisz kilka jednoczesnych połączeń od pojedynczego klienta do serwera, w pewnym momencie serwer przestanie odpowiadać na wywołania, ponieważ podejrzewa atak DDOS. Utrzyma również otwarte połączenia do czasu ich przekroczenia, aby spowolnić ataki klienta.

Wielokrotne połączenia pochodzące z różnych komputerów / adresów IP nie powinny jednak stanowić problemu.

Więcej informacji znajduje się w tym poście MSDN:

http://msdn.microsoft.com/en-us/library/bb463275.aspx

Sprawdź właściwość MaxConcurrentSession.

jurgenb
źródło
Wydaje mi się, że tak właśnie się dzieje, na podstawie wszystkiego, co widziałem, ale mam (na serwerze): <serviceThrottling maxConcurrentCalls = "150" maxConcurrentInstances = "150" maxConcurrentSessions = "150" /> <serviceDebug includeExceptionDetailInFaults = "true" /> Czy byłby jakiś monitor wydajności lub dziennik IIS, który mógłbym monitorować, aby sprawdzić, czy tak się dzieje?
Jason Kealey