Freigeben über


WCF-Dienst kann langsam unter Last skaliert werden

Dieser Artikel hilft Ihnen, den Fehler zu beheben, der auftritt, bei dem der Windows Communication Foundation (WCF)-Dienst langsam unter Dem Laden skaliert werden kann.

Originalproduktversion: Windows Communication Foundation
Ursprüngliche KB-Nummer: 2538826

Problembeschreibung

Wenn Ihr WCF-Dienst eine Reihe von Anforderungen empfängt, wird der standardmäßige IOCP-Threadpool (.NET I/O Completion Port) möglicherweise nicht so schnell wie gewünscht skaliert, und die WCF-Antwortzeit wird dadurch erhöht. Je nach Ausführungszeit und Anzahl der empfangenen Anforderungen können Sie feststellen, dass die WCF-Ausführungszeit linear um ca. 500 ms für jede empfangene Anforderung erhöht wird, bis der Prozess ausreichende IOCP-Threads erstellt hat, um die Anforderungen zu verarbeiten oder die eingehende Last aufrechtzuerhalten. Das Problem ist bei Dienstleistungen mit längeren Ausführungszeiten deutlicher. Das Skalierbarkeitsproblem des IOCP-Threadpools wird in der Regel beim ersten Laden des Prozesses nicht beobachtet.

Ursache

Drei Variablen, die auswirkungen auf ihre WCF-Dienstfähigkeit haben, um fast die gleiche Geschwindigkeit wie eingehende Anforderungen zu skalieren.

  1. WCF-Einschränkung

  2. .NET CLR-Wert Threadpool.GetMinThreads

  3. .NET CLR IOCP-Threadpoolfehler, bei dem IOCP-Threads nicht mehr in einem Muster erstellt werden, das dem Eingehenden Anforderungsvolume vor dem Threadpool.GetMinThreads Einschränkungswert entspricht.

In diesem Artikel wird beschrieben, wie Sie das Problem mit dem .NET IOCP-Threadpool #3 beheben. Wenn Aufgrund der WCF-Drosselung oder des GetMinThreads Werts Drosselungsprobleme auftreten, werden diese Drosselungen von dieser Lösung nicht vermieden. Weitere Informationen finden Sie unten im Abschnitt "Weitere Informationen", um Anleitungen zur Identifizierung Ihres Szenarios zu erhalten. Der IOCP-Threaderstellungsfehler sollte in der nächsten Version von .NET Framework nach 4.0 behoben werden. Dieses Skalierbarkeitsproblem ist im .NET CLR Worker-Threadpool nicht vorhanden.

Lösung

Durch das Verschieben der WCF-Dienstausführung in einen anderen Threadpool können Sie einen geringen Aufwand für die Implementierung dieser Lösung verursachen. Die Leistungsergebnisse variieren je nach WCF-Dienst. Testen Sie jeden WCF-Dienst auf einzelne Ergebnisse.

Notiz

Wenden Sie diese Lösung an, wenn Sie einen WCF-Listener verwenden, der den eingehenden Thread beim Warten auf den WCF-Dienstcode nicht blockiert.

WCF-Listener Empfohlene Lösung
HTTP-Synchronisierungsmodul (Standard in 3.x) – wird im integrierten Anwendungspool verwendet Wechseln Sie zum Async-Handler, und wenden Sie dann die Lösung in diesem Artikel an, oder verwenden Sie alternativ einen privaten Threadpool. (Siehe Links, die auf diese Tabelle folgen)
HTTP-Async-Modul (Standard in 4.x) – wird im integrierten Anwendungspool verwendet Wenden Sie die Codelösung in diesem Artikel an.
ISAPI - wird im Anwendungspool für den klassischen Modus verwendet Wenden Sie den privaten Threadpool an. (Siehe Links, die auf diese Tabelle folgen)
tcp.Net Wenden Sie die Codelösung in diesem Artikel an.

Wenn Sie die Lösung in diesem Artikel nach der obigen Tabelle nicht anwenden können, finden Sie ein Beispiel mit einem privaten Threadpool in einem MSDN-Artikel: Grundlagen: Synchronisierungskontexte in WCF.

Schritte zum Implementieren dieser Lösung, die den WCF-Dienst im .NET CLR Worker-Threadpool ausführt:

  1. WCF-Drosselungsschwellenwerte sollten hoch genug sein, um das erwartete Burstvolumen innerhalb akzeptabler Reaktionszeiten zu verarbeiten.

  2. Wenn Sie einen der .NET CLR-Standardthreadpools, Worker oder IOCP für Ihren WCF-Dienst verwenden, müssen Sie sicherstellen, dass die mindeste Threadanzahl (Wert, bei dem die Threaderstellungsdrosselung beginnt) auf eine Zahl, die Sie erwarten, gleichzeitig ausgeführt werden.

  3. Implementieren Sie den folgenden Code in Ihrem Dienst, der ihren WCF-Dienst dann im .NET CLR Worker-Threadpool ausführt.

    Diese Klasse wird verwendet, um die Ausführung in den .NET CLR-Workerthreadpool zu verschieben.

    public class WorkerThreadPoolSynchronizer : SynchronizationContext
    {
        public override void Post(SendOrPostCallback d, object state)
        {
         // WCF almost always uses Post
            ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
        }
    
        public override void Send(SendOrPostCallback d, object state)
        {
         // Only the peer channel in WCF uses Send
            d(state);
        }
    }
    

    Als Nächstes müssen wir eine benutzerdefinierte Attributklasse erstellen.

    [AttributeUsage(AttributeTargets.Class)]
    public class WorkerThreadPoolBehaviorAttribute : Attribute, IContractBehavior
    {
        private static WorkerThreadPoolSynchronizer synchronizer = new WorkerThreadPoolSynchronizer();
    
        void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }
    
        void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }
    
        void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
        {
        dispatchRuntime.SynchronizationContext = synchronizer;
        }
    
        void IContractBehavior.Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
        {
        }
    }
    

    Um nun das benutzerdefinierte Attribut auf Ihren WCF-Dienst anzuwenden. Beispiel:

    [WorkerThreadPoolBehavior]
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            int iSleepSec = (value * 1000);
            System.Threading.Thread.Sleep(iSleepSec);
            return string.Format("You slept for: {0} seconds", value);
        }
    }
    

Weitere Informationen

WCF verwendet den .NET CLR IOCP-Threadpool zum Ausführen des WCF-Dienstcodes. Das Problem tritt auf, wenn der .NET CLR IOCP-Threadpool in einen Zustand wechselt, in dem threads nicht schnell genug erstellt werden können, um sofort eine Reihe von Anforderungen zu verarbeiten. Die Antwortzeit erhöht sich unerwartet, da neue Threads mit einer Rate von 1 pro 500 ms erstellt werden.

Das Problem kann deutlicher werden, wenn Ihr WCF-Dienst eine Technologie verwendet, die auch den .NET CLR IOCP-Threadpool verwendet. Beispielsweise nutzt der Windows Server AppFabric-Cacheclient diesen Threadpool in geringem Umfang.

Wenn Sie die zuvor beschriebenen WCF-Einschränkungsgrenzwerte nicht erreichen, helfen Ihnen die folgenden Informationen, festzustellen, ob das Problem mit dem .NET CLR IOCP-Threadpool auftritt.

Die .NET CLR-Threadpools verwenden einen Wert, um zu bestimmen, wann die Drosselung der Erstellung von Threads beginnt. Diese Einstellung kann durch Aufrufen der ThreadPool.GetMinThreads(Int32, Int32)-Methode oder beim Analysieren eines Prozessabbilds mithilfe der ! SOS-Debuggererweiterung SOS.dll (SOS-Debugerweiterung)

0:000> ! C:\windows\Microsoft.NET\Framework64\v4.0.30319\sos.threadpool
CPU-Auslastung: 0%
Workerthread: Gesamt: 16 Ausgeführt: 0 Leerlauf: 16 MaxLimit: 250 MinLimit: 125
Arbeitsanforderung in Warteschlange: 0
Anzahl der Zeitgeber: 35
Abschlussportthread:Total: 26 Free: 0 MaxFree: 16 CurrentLimit: 28 MaxLimit: 1000 MinLimit: 125

Das beobachtete Problem liegt darin, dass der .NET CLR IOCP-Threadpool eine Bedingung eingibt, in der ein neuer Thread nur alle 500 ms (zwei pro Sekunde) vor dem MinLimit-Wert des Threadpools für den Threadpool erstellt wird. Andere erwartete Faktoren, die auch zu einer Threaderstellungsverzögerung beitragen können, wären Arbeitsspeicherdruck oder hohe CPU.

Überwachen Sie den Prozess, der Ihren WCF-Dienst hosten soll. Wenn Sie ein Problem beim Skalieren von Threads vor den von Ihnen festgelegten Mindestschwellenwerten feststellen, tritt möglicherweise das Problem mit dem .NET CLR IOCP-Threadpool auf. Um festzustellen, ob dies der Fall ist, sollte die Leistung verwendet werden, um die Prozessthreaderstellungsrate im Vergleich zur eingehenden Anforderungsrate zu überwachen. Um dies zu erreichen, protokollieren oder anzeigen Sie die folgenden Leistungsindikatoren (unten ist ein Beispiel für einen IIS -gehosteten WCF 4.0-Dienst mit einer HTTP-Bindung):

Leistungsindikator Instanz(en)
Prozess/ Threadanzahl Alle W3WP(x)-Instanzen
HTTP Service Request Queues /Arrival Rate <ApplicationPool Hosting the WCF Service(s)>
ASP.NET Apps v(4 oder 2) / Ausführungsanforderungen <WCF-Anwendungsinstanzen>
ASP.NET Apps v(4 oder 2) / Ausführungszeit anfordern <WCF-Anwendungsinstanzen>

Sie können die WCF-Leistungsindikatoren verwenden, wenn sie ebenfalls aktiviert sind:

WCF-Leistungsindikatoren

Es ist normal, eine langsam steigende Threadanzahl zu sehen, wenn die Ankunftsrate (Clientanforderungen Muster) demselben Muster folgt. Es liegt nur dann vor, wenn es eine sofortige Spitzenmenge eingehender Anforderungen gibt und die Threadanzahl langsam zu einer Rate von zwei Threads pro Sekunde steigt, während die WCF-Antwortzeit erhöht, dass ein Problem besteht.

Dieser Screenshot zeigt einen Arbeitsprozess, der nach einiger Zeit das Skalierbarkeitsproblem des .NET IOCP-Threadpools aufgetreten ist. Beim ersten Starten des Prozesses werden die IOCP-Threads normalerweise parallel zum Laden eingehender Anforderungen erstellt. In diesem AppPool (W3WP.EXE) wurden zwei WCF-Dienste ausgeführt. Ein Dienst verwendete den standardmäßigen .NET IOCP-Threadpool, der einen Platz von 100 Anforderungen bei 10:22:14 und erneut um 10:23:34 empfangen hat. Der zweite WCF-Dienst verwendete die oben genannte Problemumgehung, um im .NET Worker-Threadpool auszuführen und erhielt einen Platz von 100 Anforderungen bei 10:22:54. Nach der Eingabe dieses Zustands ist eine Prozesswiederverwendung erforderlich, um den IOCP-Threadpool in einen funktionierenden, skalierbaren Zustand wiederherzustellen.

Screenshot des Arbeitsprozesses nach auftretender Skalierbarkeit von Threadpools.