2013-02-12 24 views
6

Mam dość prostą usługę WWW WCF, hostowaną w IIS Express (ostatecznie będzie to pełne IIS) przy użyciu .Net 3.5. Metoda serwisowa jest dość nieinteresująca.Serwer 500: Zbyt wiele oczekujących bezpiecznych rozmów

[ServiceContract] 
public class MySvc 
{ 
    [OperationContract] 
    public Stuff MyMethod(string input) 
    { 
     Stuff result = DoSomething(); 
     return result; 
    } 
} 

Konfiguracja usługi jest również dość ogólna:

<system.serviceModel> 
    <services> 
     <service behaviorConfiguration="MySvcBehavior" name="MySvc"> 
      <endpoint address="" binding="wsHttpBinding" contract="MySvc"> 
       <identity> 
        <dns value="localhost"/> 
       </identity> 
      </endpoint> 
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 
     </service> 
    </services> 
    <behaviors> 
     <serviceBehaviors> 
      <behavior name="MySvcBehavior"> 
       <serviceMetadata httpGetEnabled="true"/> 
       <serviceDebug includeExceptionDetailInFaults="false"/> 
      </behavior> 
     </serviceBehaviors> 
    </behaviors> 
</system.serviceModel> 

Usługa jest zużywana przez kod z opóźnieniem w aplikacji ASPX. Jest odwołanie do usługi, co prowadzi do równie nieciekawego kodu.

MySvcClient svc = new MySvcClient(); 
Stuff result = svc.MyMethod("foo"); 

Dopóki jest to jedno żądanie na raz, wszystko działa dobrze, a kod klienta uzyskuje oczekiwany wynik. Yay.

Problem pojawia się, gdy wykonuję bardzo prymitywne testy obciążeniowe. Ładuję stronę ASPX klienta w przeglądarce, a następnie przytrzymaj klawisz F5. Oglądając okno IIS Express, na początku wyniki powracają jako status 200, ale po kilku minutach zaczynam widzieć stan 500. W tym momencie usługa będzie odpowiadać tylko statusem 500, dopóki nie uruchomię ponownie usług IIS Express. (Na podstawie czekania około 10 minut.)

Ustawienie punktu przerwania w kodzie klienta, widzę, że pełny komunikat zwrotny to "Na serwerze jest za dużo oczekujących bezpiecznych rozmów. Spróbuj ponownie później."

Ustalając punkt przerwania w kodzie serwera, stwierdzam, że mój kod nie jest nawet wywoływany. A więc zawodzi gdzieś między wywołaniem a faktycznym uruchomieniem mojego kodu.

Moje wyszukiwania internetowe nie były zbyt obiecujące, głównie prowadząc do tego samego suggestion of writing a custom binding w celu przesłonięcia właściwości maxPendingSessions i wątku rozpoczynającego się od "Ktoś powiedział mi, że jest ustawienie pliku konfiguracyjnego [bez nazwy]", co prowadzi do zerwane łącze twierdzące, że Microsoft uznał to za błąd.

Łącze o właściwości maxPendingSessions wspomina o limicie 128 połączeń z limitem czasu wynoszącym dwie minuty, a ja z pewnością zobaczę, gdzie moja metoda testowania przerwie niektóre połączenia. Czy jest to oczekiwany wynik wprawdzie złej metodologii testowania? Czy coś można zrobić w konfiguracji, aby to poprawić?

+2

Czy użyłeś instrukcji 'using'? Połączenie będzie utrzymywane otwarte do momentu usunięcia. O ile nie istnieje instrukcja 'using', czyszczenie połączeń jest określane przez moduł czyszczenia pamięci. – Caramiriel

+0

To rzeczywiście jest problem z moim kodem po stronie klienta. Ale wiadomości o statusie 500 oznaczają, że coś jest nieszczęśliwe po stronie serwera równania. – ThatBlairGuy

Odpowiedz

4

Wygląda na to, że masz miliardy otwartych połączeń na serwerze - ze względu na to, że strona klienta nie używa "połączenia" dla połączenia.

+0

Nie mogę się spierać z tym stwierdzeniem. :-) Ale skoro nie mogę zmusić klientów (innych niż moje) do dobrego pisania, jak najlepiej bronić się przed tym problemem? – ThatBlairGuy

+0

Niestety, jest to w zasadzie (nieumyślnie) atak DOS (Denial Of Service), Jedynym sposobem ochrony przed nim byłoby zerwanie połączenia po okresie bezczynności. W twoim przypadku testowym wątpię, by to pomogło, chyba że możesz ustawić czas oczekiwania na zaledwie kilka sekund. –

+0

Zasadniczo jest to również wniosek, do którego się odwołuję. – ThatBlairGuy

0

Istnieje zbyt wiele połączeń z docelowym serwerem. Istnieje kilka sposobów na prawidłowe zamknięcie połączenia. Wspomniano już, że zawsze powinno być oświadczenie using wokół proxy WCF.

Poza tym był (jest?) Błąd, który wymaga złożenia wniosku przy otwieraniu serwera proxy. Podczas tworzenia proxy daje serwerowi proxy około dwie minuty szansę na wysłanie żądania do serwera. Jeśli tak, unieważnia licznik czasu, umożliwiając wyczyszczenie proxy. Jeśli nie zostało wysłane żądanie, serwer proxy czeka na przekroczenie limitu czasu przed faktycznym zamknięciem połączenia. Możliwe, że nie w tym przypadku, ale dodane do kompletności.

Jednym ze sposobów obejścia tego problemu jest skorzystanie z buforowania (ASP.NET). System.Web.Caching.Cache jest dobrym kandydatem do tego. Pozwala na buforowanie wyników dla danego okresu (w końcu przesuwania). W ten sposób realizuje się tylko jedno żądanie, powiedzmy 2 minuty. Dzięki temu witryna/usługa internetowa są bardzo skalowalne. Na przykład, jeśli istnieje 1000 żądań na minutę, tylko jedno połączenie zostało użyte dla tego samego wywołania usługi.

Innym sposobem, aby to zrobić, jeśli żądanie jest drogie i nie warto czekać, aby złożyć wniosek z wyprzedzeniem i buforować odpowiedź (ala cron, harmonogram zadań lub po prostu inny wątek). Umożliwi to stronom szybkie pobieranie.

Poza tym serwer powinien być poprawnie skonfigurowany. Nie ma potrzeby otwierania 128 jednoczesnych połączeń z jednego klienta. Spróbuj ograniczyć to do, powiedzmy 8 połączeń na klienta. Pozwala to na obsługę wielu klientów w tym samym czasie. Spróbuj również ustawić przyzwoity limit czasu. Oczekiwanie na 5 minut, aby klient zamknął połączenie, tak naprawdę nie zwiększa przepustowości.

Rozważ także asynchronous handling of messages. Wykorzystuje czas oczekiwania usługi na I/O na obsługę dodatkowych połączeń. Nie widzę, co robi usługa WCF, więc może to nie być istotne.

+0

Uzgodnione, są to wszystkie dobre praktyki dla klienta. Potrzebuję sposobu, aby ** usługa ** broniła się przed słabo napisanym klientem. – ThatBlairGuy

0

Do tych, polecając instrukcję using: NIE należy używać instrukcji using do zamykania klientów.

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

Zamiast zamknąć klientów należy użyć poniższy fragment:

lub po prostu złapać waniliowy jeśli wolisz nie buforują wyraźnych wyjątków. Czasami chcesz wychwycić jawne wyjątki, ponieważ jeśli obiekt komunikacyjny nie jest uszkodzony, możesz ponowić operację, która oferuje najlepsze wykorzystanie zasobów.

Do Twojego problemu z oczekującymi rozmowami na serwerze stanie się to mniej powszechne po prawidłowym zamknięciu klienta, ale nadal możesz je zobaczyć, jeśli kod po stronie serwera ma długą operację, a klienci oczekują w kolejce. W takim przypadku, aby zwiększyć wartość MaxPendingChannels, musisz użyć niestandardowego powiązania w plikach konfiguracyjnych po stronie klienta i po stronie odbiorcy lub zmodyfikować programowo wiązanie po stronie odbiorcy przed uruchomieniem usługi. Domyślna wartość MaxPendingChannels to 4, jeśli korzystasz z .NET 3.5. Podano, że wartość wynosi 128 dla .NET 3.0. Oba są w mojej opinii niskie.

Zobacz http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/745b95d2-1480-4618-89f5-1339d6ee228d

Powiązane problemy