2011-07-11 12 views
6

Poniższy tekst jest próbą, aby rozwinąć i dodać kolor do tego pytania:WCF klient wywołuje serwer powiesić aż awarii zasilania

Jak mogę zapobiec występują problemy klienta z zdejmując całą usługę?

Mam zasadniczo ten scenariusz: usługa WCF jest uruchomiony z zwrotna klient ma prosty, prosty jednokierunkowy komunikacji, nie bardzo różni się od tego jednego:

public interface IMyClientContract 
{ 
    [OperationContract(IsOneWay = true)] 
    void SomethingChanged(simpleObject myObj); 
} 

Dzwonię tej metody potencjalnie tysiące razy na sekundę od usługi do tego, co ostatecznie będzie około 50 jednocześnie połączonych klientów, z możliwie jak najmniejszym opóźnieniem (< 15 ms byłoby miłe). Działa to dobrze, dopóki nie ustawię punktu przerwania na jeden aplikacji klienckich połączonych z serwerem, a następnie wszystko zawiesza się po około 2-5 sekundach, gdy usługa się zawiesza i żaden z pozostałych klientów nie otrzymuje żadnych danych przez około 30 sekund, dopóki usługa rejestruje zdarzenie błędu połączenia i odłącza obrażającego klienta. Następnie wszyscy pozostali klienci kontynuują wesołe otrzymywanie wiadomości.

Zrobiłem badania nad usługąDziękuję, poprawianie współbieżności, ustawianie wątków minimalnych wątków, tajnych sosów WCF i całych 9 jardów, ale pod koniec dnia ten artykuł MSDN - WCF essentials, One-Way Calls, Callbacks and Events opisuje dokładnie problem, który mam, bez tworzenia Rekomendacja.

Trzecie rozwiązanie, które pozwala usłudze bezpiecznie odwołać się do klienta, powoduje, że operacje kontraktu oddzwonienia są skonfigurowane jako operacje jednokierunkowe. Dzięki temu usługa może oddzwonić nawet wtedy, gdy współbieżność jest ustawiona na jednowątkową, ponieważ nie będzie żadnej odpowiedzi na żądanie blokady.

ale wcześniej w artykule opisuje problem widzę, tylko z perspektywy klienta

Kiedy jednokierunkowe połączenia dotrzeć usługę, mogą one nie być wysłane wszystkie naraz i may należy umieścić w kolejce po stronie usługi, aby były wysyłane pojedynczo, wszystko zgodnie z zachowaniem w trybie współbieżności usługi i trybem sesji. Ile komunikatów (czy to w jedną stronę, czy w odpowiedzi na żądanie), czy usługa jest gotowa do umieszczenia w kolejce, jest produktem skonfigurowanego kanału i trybu niezawodności. Jeśli liczba oczekujących wiadomości przekroczyła pojemność kolejki, wtedy klient będzie blokował, nawet przy wydawaniu jednokierunkowego połączenia

Mogę tylko przypuszczać, że jest odwrotnie, liczba oczekujących wiadomości do klienta Przekroczono pojemność kolejki i wątek jest teraz wypełniony wątkami próbującymi wywołać tego klienta, które są teraz zablokowane.

Jaki jest właściwy sposób postępowania z tym? Czy powinienem zbadać sposób sprawdzenia, ile wiadomości są umieszczane w kolejce w warstwie komunikacji usług na klienta i przerwać połączenie po osiągnięciu określonego limitu?

Wygląda na to, że jeśli sama usługa WCF blokuje zapełnianie kolejki, wszystkie strategie asynchroniczne/oneway/fire-and-forget, które mogę wprowadzić w ramach usługi, będą nadal blokowane za każdym razem, gdy kolejka jednego klienta się zapełni. .

+0

Longform w/szczegóły są doceniane, ale podsumowanie exec/TL; DR również będzie docenione. –

+0

Czy sprawdziłeś liczniki wydajności lub śledzoną aktywność sieciową, aby sprawdzić, ile żądań zostało zablokowanych na wstrzymanym kliencie? –

+1

Eksperymentuję z [fire-and-forget] (http://www.yoda.arachsys.com/csharp/threads/threadpool.shtml) i to faktycznie pokazuje pewien postęp. Zawijam synchronicznego delegata DynamicInvoke z blokiem catch, a następnie używając delegate.Target.GetHashCode() jako klucza do słownika bieżących połączeń, aby zidentyfikować obrażający kanał klienta, używając go do zabicia połączenia klienta ... wiem, czy to jest skalowalne –

Odpowiedz

1

Aktualizacja:

I wdrożone konfigurację Fire-and-forget zadzwonić kanał zwrotnej klienta i serwer nie blokuje już raz bufor napełnia klientowi

MyEvent jest wydarzeniem z delegatem, który odpowiada jeden z metod określonych w umowie klienta WCF, gdy łączą ja w zasadzie dodanie zwrotnego do wydarzenia

MyEvent += OperationContext.Current.GetCallbackChannel<IFancyClientContract>().SomethingChanged 

itp ... a następnie wysłać t jego dane do wszystkich klientów, robię następujące

//serialize using protobuff 
using (var ms = new MemoryStream()) 
{ 
    ProtoBuf.Serializer.Serialize(ms, new SpecialDataTransferObject(inputData)); 
    byte[] data = ms.GetBuffer(); 
    Parallel.ForEach(MyEvent.GetInvocationList(), p => ThreadUtil.FireAndForget(p, data)); 
} 

w klasie ThreadUtil zrobiłem zasadniczo następujące zmiany kodu zdefiniowanego w artykule fire-and-foget

static void InvokeWrappedDelegate(Delegate d, object[] args) 
{ 
    try 
    { 
     d.DynamicInvoke(args); 
    } 
    catch (Exception ex) 
    { 
     //THIS will eventually throw once the client's WCF callback channel has filled up and timed out, and it will throw once for every single time you ever tried sending them a payload, so do some smarter logging here!! 
     Console.WriteLine("Error calling client, attempting to disconnect."); 
     try 
     { 
      MyService.SingletonServiceController.TerminateClientChannelByHashcode(d.Target.GetHashCode());//this is an IContextChannel object, kept in a dictionary of active connections, cross referenced by hashcode just for this exact occasion 
     } 
     catch (Exception ex2) 
     { 
      Console.WriteLine("Attempt to disconnect client failed: " + ex2.ToString()); 
     } 
    } 
} 

I don nie ma żadnych dobrych pomysłów, jak przejść i zabić wszystkie oczekujące pakiety, które serwer wciąż czeka, aby sprawdzić, czy zostaną dostarczone. Kiedy otrzymam pierwszy wyjątek, teoretycznie powinienem móc zakończyć wszystkie pozostałe żądania w jakiejś kolejce, ale ta konfiguracja jest funkcjonalna i spełnia cele.

+0

Nie wiem, czy zdobywam cokolwiek poprzez użycie Parallel.ForEach tutaj –

1

Niewiele wiadomo na temat wywołań zwrotnych od klientów, ale brzmi to podobnie do ogólnych problemów z blokowaniem kodu wcf. Często rozwiązuję te problemy, tworząc narzędzie BackgroundWorker i wykonując wywołanie klienta w wątku. W tym czasie główny wątek liczy, jak długo trwa wątek potomny.Jeśli dziecko nie skończyło się w ciągu kilku milisekund, główny wątek po prostu się porusza i porzuca wątek (w końcu umiera sam, więc nie ma wycieku pamięci). Jest to w zasadzie to, co Mr Graves sugeruje zwrotem "pożar i zapomnij".

+0

+1! Zastanawiałem się, jak rozwiązać ten sam problem podczas gry z subskrybowaniem publikacji. Jeśli klient blokuje się w nieskończoność, nie umierając, zabijał moją usługę. Nie myślałem o zrobieniu tego :) – Franchesca

Powiązane problemy