2016-03-03 9 views
7

Durante l'esecuzione delle chiamate all'API SOAP AXL di Cisco tramite WCF viene utilizzato un elevato numero di CPU. Comincio creando un client del modello di servizio utilizzando classi generate da wsdl. Sto usando basichttpbinding e transfermode come buffer. Quando si esegue una chiamata, la CPU raggiunge il limite massimo e un profilo CPU mostra che il 96% del tempo della CPU è a [email protected] da clr.dll chiamato dopo chiamate come base.Channel.getPhone(request);. Più correttamente, la chiamata espande il core della CPU su cui è in esecuzione il processo.WCF alza CPU durante l'attesa della funzione _TransparantProxyStub_CrossContext durante la chiamata

Ecco un elemento di cattura della creazione client dal WSDL generare

[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) 
    { 
    } 

    ... 

Questo è come genero il cliente:

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; 
    } 
} 

Il codice WSDL generato per l'API AXL è molto grande. Sia le chiamate iniziali che quelle successive hanno il problema della CPU, sebbene le chiamate successive siano più veloci. C'è altro che posso fare per eseguire il debug di questo problema? C'è un modo per ridurre questo elevato utilizzo della CPU?

Aggiornamento

Un po 'più di informazioni con la grazia:

Ho creato le classi C# in questo modo:

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

È necessario scaricare il WSDL per AXL API di Cisco da un sistema di gestione delle chiamate. Sto usando la versione 10.5 dell'API. Credo che un importante rallentamento sia legato all'elaborazione XML. Il WSDL per l'API è enorme con le classi risultanti che creano 538406 linee di codice!

Aggiornamento 2

ho acceso WCF tracciando con tutti i livelli. La maggiore differenza di tempo è nell'azione di processo tra "Un messaggio è stato scritto" e "Inviato un messaggio su un canale" in cui passa quasi un intero minuto tra queste due azioni. Altre attività (costrutto canale, open clientbase e close clientbase) vengono eseguite tutte relativamente veloci.

Update 3

Ho fatto due modifiche alle classi client generato. Innanzitutto, ho rimosso lo ServiceKnownTypeAttribute da tutti i contratti operativi. In secondo luogo, ho rimosso XmlIncludeAtribute da alcune classi serializzabili. Queste due modifiche hanno ridotto la dimensione del file del client generato di oltre il 50% e hanno avuto un impatto minimo sui tempi di test (una riduzione di circa 10 secondi su un risultato del test degli anni '70).

Ho anche notato che ho circa 900 contratti di operazione per un'unica interfaccia di servizio e endpoint. Ciò è dovuto al wsdl per l'API AXL che raggruppa tutte le operazioni in un singolo spazio dei nomi. Sto pensando di rompere questo, ma ciò significherebbe creare più clientbase che implementerebbero ciascuna un'interfaccia ridotta e finiranno per rompere tutto ciò che implementa questa libreria wcf.

Update 4

Sembra che il numero di operazioni è il problema centrale. Sono stato in grado di separare le operazioni e le definizioni dell'interfaccia in base al verbo (ad es.ottiene, aggiunge, ecc.) nella propria base client e nell'interfaccia (un processo molto lento che utilizza il testo sublime e regex come resharper e codemaid non possono gestire il file di grandi dimensioni che è ancora 250K + linee). Un test del client "Get" con circa 150 operazioni definite ha comportato un'esecuzione di 10 secondi per getPhone rispetto a un precedente risultato di 60 secondi. Questo è ancora molto più lento di quanto dovrebbe essere, semplicemente creando questa operazione nei risultati del violinista in un'esecuzione di 2 secondi. La soluzione probabilmente ridurrà ulteriormente il conteggio delle operazioni cercando di separare ulteriormente le operazioni. Tuttavia, questo aggiunge un nuovo problema di rompere tutti i sistemi che hanno utilizzato questa libreria come un singolo client.

+0

Poiché non si tratta di una risposta definitiva, condividerò un'esperienza simile. Avevo creato un wcf con molte operazioni e grandi wsdl. Ho avuto un problema simile con l'utilizzo della CPU alta. Avevo risolto il problema, impostando l'assembly per la serializzazione sul progetto wcf. Nelle proprietà del progetto, sezione build, puoi provare a impostare "Genera assembly di serializzazione" su "On" e anche impostare la "Documentazione Xml" nella cartella "bin \ release" o "bin \ debug" a seconda di come stai costruire la tua soluzione. Fammi sapere se funziona così posso scrivere una risposta e pubblicare di più su questa soluzione alternativa. –

+0

Non ho ottenuto alcun aumento delle prestazioni dall'impostazione di queste due opzioni. Stavo eseguendo test unitari nella configurazione di rilascio con serializzazione impostata su auto che in base a questo (https://stackoverflow.com/questions/9187248/when-to-change-the-generate-serialization-assembly-value) indica che la serializzazione è attiva già. La documentazione Xml serve per tradurre i commenti in afik di intelligenza, ma ho provato comunque. –

risposta

1

Ho finalmente risolto questo problema. La causa principale sembra essere il numero di operazioni. Dopo aver diviso il client generato da 900 + operazioni a 12 ciascuno (seguendo le indicazioni da this question) sono stato in grado di ridurre il tempo di processore speso per generare richieste quasi a zero.

Questo è il processo finale per ottimizzare il client del servizio generato da AXL wsdl di Cisco:

generare il codice client che utilizza WSDL in questo modo:

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

elaborare il file client generato per rompere nei client secondari:

Ho creato this script per elaborare il codice generato. Questo script esegue le seguenti operazioni:

  1. Rimuovere ServiceKnownType, FaultContract e XmlInclude attributi.

Questi sono utili per l'elaborazione xml, ma le classi generate sembrano non corrette da quello che ho capito. Il serviceknowntype, ad esempio, è identico per tutte le operazioni anche se molti dei tipi noti sono unici per ciascuna opera. Ciò riduce la dimensione totale del file generato da 500K + linee a 250 K + con un minore aumento delle prestazioni nel tempo di istanziazione del client.

  1. Separare i contratti di funzionamento dall'interfaccia e dai metodi dalla base di clienti che implementa l'interfaccia.

  2. Creare client secondari ciascuno con 12 operazioni e la rispettiva implementazione.

Questi client secondari hanno tre parti principali. La prima parte è una classe parziale del client client di base originale. Voglio che questa soluzione sia retrocompatibile quindi ho qui dei metodi che fanno riferimento al client secondario in modo che le chiamate al vecchio super-client funzionino ancora chiamando il nuovo client secondario. Un accesso get statico avvierà il client secondario se viene fatto riferimento a una delle operazioni implementate. Vi sono anche eventi aggiunti per quando viene chiamato close o abort in modo che i client secondari possano ancora eseguire queste operazioni.

La seconda e la terza parte del client secondario sono l'interfaccia e la classe client secondario che implementa le 12 operazioni.

Ho quindi rimosso l'interfaccia ei metodi client dal client generato originale. Ho sostituito i costruttori client per il client originale per archiviare semplicemente i dati di binding e endpoint per i client secondari da utilizzare quando necessario. Le chiamate di chiusura e interruzione sono state ricreate come richiami di eventi a cui ogni client secondario si iscriveva quando è istanziato.

Infine, ho spostato l'autenticazione su un comportamento dell'endpoint personalizzato simile a quello described here. L'utilizzo del IClientMessageInspector per inviare l'intestazione di autenticazione salva immediatamente in una chiamata di andata e ritorno al server in cui WCF ama inviare una richiesta anonima prima di autenticare. Questo mi dà all'incirca un aumento di 2 secondi a seconda del server.

Nel complesso, ho un aumento delle prestazioni da 70 a 2,5 s.

Problemi correlati