2016-03-03 26 views
7

Mam coraz większe obciążenie procesora podczas wykonywania wywołań Cisco API AXL SOAP za pomocą WCF. Zacznę od utworzenia bazy klientów modelu usług przy użyciu wygenerowanych klas z pliku wsdl. Używam basichttpbinding i transfermode jako buforowane. Podczas wykonywania połączenia maksymalny czas pracy procesora jest zbyt duży, a profil procesora pokazuje, że 96% czasu procesora wynosi [email protected] od clr.dll, który jest wywoływany po wywołaniach, takich jak base.Channel.getPhone(request);. Dokładniej, wywołanie maksymalizuje rdzeń procesora, na którym działa proces.WCF max CPU podczas oczekiwania na funkcję _TransparantProxyStub_CrossContext podczas wywołania

Oto wycinek stworzenia klienta z WSDL generowania

[System.Diagnostics.DebuggerStepThroughAttribute()] 
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 
public partial class AXLPortClient : System.ServiceModel.ClientBase<AxlNetClient.AXLPort>, AxlNetClient.AXLPort 
{ 

    public AXLPortClient() 
    { 
    } 

    public AXLPortClient(string endpointConfigurationName) : 
      base(endpointConfigurationName) 
    { 
    } 

    ... 

ten sposób tworzę kliencie:

public class AxlClientFactory : IAxlClientFactory 
{ 
    private const string AxlEndpointUrlFormat = "https://{0}:8443/axl/"; 

    public AXLPortClient CreateClient(IUcClientSettings settings) 
    { 
     ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true; 
     ServicePointManager.Expect100Continue = false;      

     var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport); 
     basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; 

     basicHttpBinding.MaxReceivedMessageSize = 20000000; 
     basicHttpBinding.MaxBufferSize = 20000000; 
     basicHttpBinding.MaxBufferPoolSize = 20000000; 

     basicHttpBinding.ReaderQuotas.MaxDepth = 32; 
     basicHttpBinding.ReaderQuotas.MaxArrayLength = 20000000; 
     basicHttpBinding.ReaderQuotas.MaxStringContentLength = 20000000; 

     basicHttpBinding.TransferMode = TransferMode.Buffered; 
     //basicHttpBinding.UseDefaultWebProxy = false; 

     var axlEndpointUrl = string.Format(AxlEndpointUrlFormat, settings.Server); 
     var endpointAddress = new EndpointAddress(axlEndpointUrl); 
     var axlClient = new AXLPortClient(basicHttpBinding, endpointAddress); 
     axlClient.ClientCredentials.UserName.UserName = settings.User; 
     axlClient.ClientCredentials.UserName.Password = settings.Password; 
     return axlClient; 
    } 
} 

Wygenerowany kod WSDL dla API AXL jest bardzo duża. Zarówno początkowe, jak i kolejne wywołania mają problem z procesorem, chociaż kolejne połączenia są szybsze. Czy jest coś jeszcze, co mogę zrobić, aby rozwiązać ten problem? Czy istnieje sposób na zmniejszenie tego wysokiego obciążenia procesora?

Aktualizacja

Nieco więcej informacji o dobroci:

I utworzeniu klasy C# tak:

svcutil AXLAPI.wsdl AXLEnums.xsd AXLSoap.xsd /t:code /l:C# /o:Client.cs /n:*,AxlNetClient 

Musisz pobrać WSDL dla AXL API Cisco z systemu zarządzania połączeniami. Używam wersji API 10.5. Uważam, że poważne spowolnienie wiąże się z przetwarzaniem XML. WSDL dla interfejsu API jest ogromny, a otrzymane klasy tworzą 538406 linii kodu!

Aktualizacja 2

mam włączone WCF śledzenie wszystkich poziomach. Największą różnicą czasową jest aktywność w działaniu procesu pomiędzy "Napisano wiadomość" i "Wysłano wiadomość przez kanał", w której upływa prawie jedna minuta między tymi dwoma działaniami. Inne działania (budowa kanału, otwarta baza klientów i bliska baza klientów) działają stosunkowo szybko.

Update 3

Zrobiłem dwie zmiany wygenerowanych klas klienckich. Najpierw usunąłem ServiceKnownTypeAttribute ze wszystkich umów dotyczących operacji. Po drugie, usunąłem XmlIncludeAtribute z niektórych klas serializowalnych. Te dwie zmiany zmniejszyły rozmiar pliku wygenerowanego klienta o ponad 50% i miały niewielki wpływ na czasy testów (zmniejszenie o około 10 sekund w wyniku testu z lat 70.).

Zauważyłem również, że mam około 900 umów operacyjnych dla pojedynczego interfejsu usługi i punktu końcowego. Wynika to z faktu, że wsdl dla AXL API grupuje wszystkie operacje w jednym obszarze nazw. Zastanawiam się nad rozbiciem tego, ale to oznaczałoby stworzenie wielu baz klientów, które zaimplementowałyby zredukowany interfejs i ostatecznie zepsują wszystko, co implementuje tę bibliotekę wcf.

Update 4

Wygląda na to liczba operacji jest głównym problemem. Byłem w stanie oddzielić operacje i definicje interfejsów według czasownika (np.pobiera, dodaje itp.) do własnej bazy klientów i interfejsu (bardzo powolny proces z wykorzystaniem wysublimowanego tekstu i regex, ponieważ resharper i codemaid nie potrafią obsłużyć dużego pliku, który wciąż ma 250k + linii). Test klienta "Get" z około 150 zdefiniowanymi operacjami zakończył się 10-sekundowym wykonaniem dla getPhone w porównaniu do poprzedniego wyniku 60 sekund. To wciąż jest o wiele wolniejsze, niż powinno być po prostu zrobienie tej operacji w wyniku fiddlera w 2 sekundowym wykonaniu. Rozwiązanie prawdopodobnie zmniejszy liczbę operacji jeszcze bardziej, próbując dalej rozdzielić operacje. Dodaje to jednak nowy problem z rozbijaniem wszystkich systemów, które używały tej biblioteki jako pojedynczego klienta.

+0

Ponieważ nie jest to rozstrzygająca odpowiedź, podzielę się podobnym doświadczeniem. Stworzyłem wcf z wieloma operacjami i dużymi wsdl. Miałem podobny problem z wysokim wykorzystaniem procesora. Rozwiązałem problem, ustawiając zestaw do serializacji w projekcie wcf. W sekcji właściwości projektu, kompilacja, możesz spróbować ustawić "Generuj serializację" na "Włącz", a także ustawić "Dokumentację Xml" w folderze "bin \ release" lub "bin \ debug" w zależności od tego, jak się masz budowanie twojego rozwiązania. Daj mi znać, jeśli pracuję, więc mogę napisać odpowiedź i opublikować więcej informacji na temat tego obejścia. –

+0

Nie dostałem wzrostu wydajności od ustawienia tych dwóch opcji. Uruchomiłem testy jednostkowe w konfiguracji wydania z serializacją ustawioną na auto, która zgodnie z tym (https://stackoverflow.com/questions/9187248/when-to-change-the-generate-serialization-assembly-value) oznacza, że ​​serializacja jest włączona już. Dokumentacja Xml służy do tłumaczenia komentarzy do intellisence afik, ale i tak próbowałem. –

Odpowiedz

1

W końcu rozwiązałem ten problem. Główną przyczyną wydaje się liczba operacji. Po podzieleniu wygenerowanego klienta z ponad 900 operacji na 12 (według wskazówek od this question) udało mi się zredukować czas procesora spędzony na generowaniu żądań do niemal zera.

Jest to ostatni proces optymalizacji klienta generowane usług od Cisco AXL WSDL:

wygenerować kod klienta za pomocą WSDL tak:

svcutil AXLAPI.wsdl AXLEnums.xsd AXLSoap.xsd /t:code /l:C# /o:Client.cs /n:*,AxlNetClient 

Process wygenerowany plik klient zerwać do klientów podrzędnych:

Utworzono this script w celu przetworzenia wygenerowanego kodu. Ten skrypt wykonuje następujące operacje:

  1. Usuń ServiceKnownType, FaultContract i XmlInclude atrybuty.

Są one przydatne do przetwarzania xml, ale wygenerowane klasy wydają się nieprawidłowe z tego, co rozumiem. Typ serviceknowntype jest identyczny dla wszystkich operacji, mimo że wiele z nich jest unikalnych dla każdej operacji. Zmniejsza to całkowity rozmiar wygenerowanego pliku z 500K + linii do 250K + przy niewielkim zwiększeniu wydajności w czasie tworzenia klienta.

  1. Oddzielić umowy operacyjne od interfejsu i metod od bazy klientów, które implementują interfejs.

  2. Twórz podklienci po 12 operacji i ich implementacji.

Podklienci mają trzy główne części. Pierwsza część to częściowa klasa oryginalnego klienta klienckiego. Chcę, aby to rozwiązanie było zgodne z poprzednimi wersjami, więc mam tutaj metody odwołujące się do subklienta, aby wywołania starego super-klienta nadal działały, wywołując nowego subklienta. Statyczny akcesor uzyskujący inicjuje podklienta, jeśli odwołuje się do którejkolwiek z jego zaimplementowanych operacji. Dodano również zdarzenia, które powodują zamknięcie lub przerwanie wywołania, aby podklienci mogli nadal wykonywać te operacje.

Druga i trzecia część subklienta to klasa interfejsu i podklasa, która implementuje 12 operacji.

Następnie usunąłem interfejs i metody klienta z oryginalnego wygenerowanego klienta. Wymieniłem konstruktory klienckie dla oryginalnego klienta, aby po prostu przechowywać dane o powiązaniu i punkcie końcowym dla subklientów do użycia w razie potrzeby. Zamykanie i przerywanie wywołań zostało odtworzone jako wywołania zdarzeń, które każdy subskrybent zasubskrybuje po utworzeniu instancji.

Na koniec przeszedłem uwierzytelnianie do niestandardowego zachowania punktu końcowego podobnego do tego, co jest described here. Użycie IClientMessageInspector do wysłania nagłówka uwierzytelnienia natychmiast zapisuje w jednym obietku połączenie z serwerem, na którym WCF lubi wysyłać anonimowe żądanie najpierw przed uwierzytelnieniem. To daje mi około 2-sekundowy wzrost w zależności od serwera.

Ogólnie rzecz biorąc, mam wzrost wydajności od 70s do 2.5s.