2011-10-20 23 views
18

Postanowiłem więc nieco zwiększyć wydajność w mojej aplikacji WCF i spróbować buforować kanały i ChannelFactory. Mam dwa pytania na ten temat, które muszę wyjaśnić, zanim zacznę.Kanał WCF i ChannelFactory Caching

1) Czy obiekt ChannelFactory powinien zostać wdrożony jako singleton?

2) Nie jestem pewien, jak cache/ponowne wykorzystanie poszczególnych kanałów. Czy masz przykłady tego, jak możesz to zrobić?

Należy pamiętać, że moja usługa WCF jest wdrażana jako samodzielna aplikacja z jednym punktem końcowym.

EDIT:

Dziękuję za odpowiedzi. Nadal mam kilka pytań ...

1) Sądzę, że jestem zdezorientowany co do miejsca, w którym powinno wystąpić buforowanie. Dostarczam interfejs API klienta, który używa tego kodu do innego działu w naszej firmie. Czy to buforowanie występuje na kliencie?

2) API klienta będzie używane jako część aplikacji Silverlight, czy to coś zmieni? W szczególności, jakie mechanizmy buforowania są dostępne w takim scenariuszu?

3) Nadal nie mam jasności co do projektu metody GetChannelFactory. Jeśli mam tylko jedną usługę, powinienem utworzyć i buforować tylko jeden kanał ChannelFactory?

ja nadal nie wprowadziły żadnych funkcji buforowania (bo jestem kompletnie zdezorientowany o tym, jak należy to zrobić!), Ale tutaj jest to, co mam do proxy klienta do tej pory:

namespace MyCompany.MyProject.Proxies 
{ 
    static readonly ChannelFactory<IMyService> channelFactory = 
     new ChannelFactory<IMyService>("IMyService"); 

    public Response DoSomething(Request request) 
    { 
     var channel = channelFactory.CreateChannel(); 

     try 
     { 
      Response response = channel.DoSomethingWithService(request); 
      ((ICommunicationObject)channel).Close(); 
      return response; 
     } 
     catch(Exception exception) 
     { 
      ((ICommenicationObject)channel).Abort(); 
     } 
    } 
} 
+0

Dla numeru 3 należy utworzyć tylko jedną fabrykę kanałów. Zasadniczo, będziesz mieć fabrykę jednego kanału dla każdej usługi endpiont, którą masz. W moim przypadku mamy około 6 dotychczasowych, rozłożonych głównie na 2 poziomach. W twoim przypadku, jeśli masz tylko jedną usługę, której używa jedna aplikacja, możesz po prostu zrobić to, co robisz powyżej. Powyższy kod jest na dobrej drodze. Buforowanie będzie oparte na potrzebach aplikacji. – Tim

+0

@Tim-podziękowania za całą pomoc. Naprawdę to doceniam! Wydaje mi się, że w moim przypadku fakt, że moja usługa była tak "prosta" powodował, że nie mogłem pomylić się z innymi przykładami, w których było wiele punktów końcowych. Ja = mniej zmieszany teraz = Tim świetnie się spisał! Dzięki stary! – Didaxis

+0

Nie ma za co. Cieszę się, że mogłem pomóc - szczęśliwe kodowanie! – Tim

Odpowiedz

20

użyć ChannelFactory, aby utworzyć instancję fabryki, a następnie buforować tę instancję. Następnie można utworzyć kanały komunikacyjne zgodnie z potrzebami/pożądaniami z pamięci podręcznej.

Czy potrzebujesz wielu fabryk kanału (np. Czy jest wiele usług)? Z mojego doświadczenia wynika, że ​​największe korzyści z wydajności mają. Utworzenie kanału jest dość niedrogim zadaniem; ustawia wszystko na początku, co wymaga czasu.

Nie będę buforował poszczególnych kanałów - będę je tworzyć, używać ich do operacji, a następnie je zamykać. Jeśli je buforujesz, mogą upłynąć limit czasu, a kanał popełni błąd, a następnie musisz go przerwać i utworzyć nowy.

Nie wiem, dlaczego chcesz zastosować singleton do implementacji ChannelFactory, zwłaszcza jeśli zamierzasz go utworzyć i wstawić do pamięci podręcznej, a jest tylko jeden punkt końcowy.

Opublikuję przykładowy kod później, gdy będę miał nieco więcej czasu.

UPDATE: Kod Przykłady

Oto przykład, jak ja to realizowane za projekt w pracy. Użyłem ChannelFactory<T>, ponieważ aplikacja, którą tworzyłem, jest aplikacją n-warstwową z kilkoma usługami, a kolejne zostaną dodane. Celem było stworzenie prostego sposobu na utworzenie klienta raz w życiu, a następnie tworzenie kanałów komunikacji w razie potrzeby. Podstawy tego pomysłu nie są moje (otrzymałem je z artykułu w Internecie), choć zmodyfikowałem implementację do moich potrzeb.

Mam statyczną klasę pomocniczą w mojej aplikacji, aw jej obrębie mam słownik i metodę tworzenia kanałów komunikacyjnych z fabryki channelf.

Słownik jest następujący (obiekt jest wartością, ponieważ będzie zawierał różne fabryki kanałów, po jednym dla każdej usługi). Wstawiam "Pamięć podręczna" w przykładzie jako rodzaj elementu zastępczego - zamień składnię na dowolny mechanizm buforowania, którego używasz.

public static Dictionary<string, object> OpenChannels 
{ 
    get 
    { 
     if (Cache["OpenChannels"] == null) 
     { 
      Cache["OpenChannels"] = new Dictionary<string, object>(); 
     } 

     return (Dictionary<string, object>)Cache["OpenChannels"]; 
    } 
    set 
    { 
     Cache["OpenChannels"] = value; 
    } 
} 

Dalej jest metoda tworzenia kanału komunikacji z instancji fabryki. Metoda sprawdza, czy fabryka istnieje najpierw - jeśli nie, tworzy ją, umieszcza w słowniku, a następnie generuje kanał. W przeciwnym razie po prostu generuje kanał z buforowanej instancji fabryki.

public static T GetFactoryChannel<T>(string address) 
{ 

    string key = typeof(T.Name); 

    if (!OpenChannels.ContainsKey(key)) 
    { 
     ChannelFactory<T> factory = new ChannelFactory<T>(); 
     factory.Endpoint.Address = new EndpointAddress(new System.Uri(address)); 
     factory.Endpoint.Binding = new BasicHttpBinding(); 
     OpenChannels.Add(key, factory); 
    } 

    T channel = ((ChannelFactory<T>)OpenChannels[key]).CreateChannel(); 

    ((IClientChannel)channel).Open(); 

    return channel; 
} 

Usunąłem ten przykład z tego, z czego korzystam w pracy. W tej metodzie można wiele zrobić - można obsługiwać wiele powiązań, przypisać poświadczenia do uwierzytelniania itp. Jest to prawie jedno centrum zakupowe do generowania klienta.

Wreszcie, kiedy używam go w aplikacji, generalnie utworzę kanał, wykonam swoją działalność i zamknę (lub przerwam jeśli zajdzie taka potrzeba). Na przykład:

IMyServiceContract client; 

try 
{ 
    client = Helper.GetFactoryChannel<IMyServiceContract>("http://myserviceaddress"); 

    client.DoSomething(); 

    // This is another helper method that will safely close the channel, 
    // handling any exceptions that may occurr trying to close. 
    // Shouldn't be any, but it doesn't hurt. 
    Helper.CloseChannel(client); 
} 
catch (Exception ex) 
{ 
    // Something went wrong; need to abort the channel 
    // I also do logging of some sort here 
    Helper.AbortChannel(client); 
} 

Mam nadzieję, że powyższe przykłady dadzą ci coś do zrobienia. Używam czegoś podobnego od około roku w środowisku produkcyjnym i działa bardzo dobrze. 99% wszystkich napotkanych problemów zwykle dotyczyło czegoś spoza aplikacji (klientów zewnętrznych lub źródeł danych nie będących pod naszą bezpośrednią kontrolą).

Daj mi znać, jeśli coś nie jest jasne lub masz dodatkowe pytania.

+0

Dzięki Tim. Naprawdę dostarczyłeś cennych informacji. Na pewno będę wyglądał na twój przykład! – Didaxis

+0

@ user384080 - kod jest w mojej odpowiedzi. Jeśli to nie jest jasne, daj mi znać. Dzięki. – Tim

+0

@Tim W implementacji występuje błąd. Buforujesz fabryki według typu umowy, bez względu na to, jaki jest adres. Powinieneś mieć klucz, który zawiera zarówno typ kontraktu, jak i adres. – Anubis

5

Zawsze można po prostu dokonać ChannelFactory statyczne dla każdego WCF umowy ...

Trzeba mieć świadomość, że z .NET 3.5 obiekty proxy łączy się ze względów wydajnościowych przez fabrykę kanału. Wywołanie metody ICommunicationObject.Close() faktycznie zwraca obiekt do puli w nadziei, że zostanie ponownie użyty.

Spojrzałbym na profilera, jeśli chcesz przeprowadzić jakąś optymalizację, jeśli możesz zapobiec tylko jednemu wywołaniu IO w twoim kodzie, może to znacznie przewyższyć wszelkie optymalizacje, które wprowadzisz w fabryce kanału. Nie wybieraj obszaru do optymalizacji, użyj narzędzia profilowania, aby znaleźć miejsce docelowe optymalizacji. Jeśli masz na przykład bazę danych SQL, prawdopodobnie znajdziesz w swojej kwerendzie jakieś nisko wiszące owoce, które zwiększą wydajność rzędu wielkości, jeśli nie zostały jeszcze zoptymalizowane.

3

Tworzenie kanału kosztuje tak bardzo wydajność. faktycznie, WCF ma już mechanizm pamięci podręcznej dla ChannelFactory, jeśli używasz ClientBase w kliencie zamiast czystego ChannelFactory. Ale pamięć podręczna wygasa, jeśli wykonasz kilka operacji (proszę google go o szczegóły, jeśli chcesz). Dla problemu ErOx mam inne rozwiązanie, które moim zdaniem jest lepsze. patrz poniżej:


namespace ChannelFactoryCacheDemo 
{ 
    public static class ChannelFactoryInitiator 
    { 
     private static Hashtable channelFactories = new Hashtable(); 

     public static ChannelFactory Initiate(string endpointName) 
     { 
      ChannelFactory channelFactory = null; 

      if (channelFactories.ContainsKey(endpointName))//already cached, get from the table 
      { 
       channelFactory = channelFactories[endpointName] as ChannelFactory; 
      } 
      else // not cached, create and cache then 
      { 
       channelFactory = new ChannelFactory(endpointName); 
       lock (channelFactories.SyncRoot) 
       { 
        channelFactories[endpointName] = channelFactory; 
       } 
      } 
      return channelFactory; 
     } 
    } 
    class AppWhereUseTheChannel 
    { 
     static void Main(string[] args) 
     { 
      ChannelFactory channelFactory = ChannelFactoryInitiator.Initiate("MyEndpoint"); 
     } 
    } 

    interface IMyContract { } 
} 

Możesz samodzielnie dostosować logikę i parametry metody Inicjuj, jeśli masz inne wymagania. ale ta klasa inicjatora nie ogranicza się tylko do jednego punktu końcowego. jest potężny dla wszystkich punktów końcowych w twojej aplikacji. ufnie. to działa dobrze dla ciebie. BTW. to rozwiązanie nie jest ode mnie. mam to z książki.

+0

Zwróć uwagę, że 'blokada' jest używana niepoprawnie. Blokada powinna również zostać podjęta przy wywołaniu klawisza 'ContainsKey'. –