Freigeben über


Bündelung

Das Beispiel "Pooling " veranschaulicht, wie Sie Windows Communication Foundation (WCF) erweitern, um objektpooling zu unterstützen. Das Beispiel veranschaulicht das Erstellen eines Attributs, das syntaktisch und semantisch der Attributfunktionalität von Enterprise Services ähnelt ObjectPoolingAttribute . Objektpooling kann eine dramatische Verstärkung der Leistung einer Anwendung bieten. Es kann jedoch den gegenteiligen Effekt haben, wenn er nicht ordnungsgemäß verwendet wird. Mit objektpooling können Sie den Aufwand für die Neuerstellen häufig verwendeter Objekte verringern, die eine umfangreiche Initialisierung erfordern. Wenn ein Aufruf einer Methode für ein pooliertes Objekt jedoch eine beträchtliche Zeit in Anspruch nimmt, werden zusätzliche Anforderungen in die Warteschlange gestellt, sobald die maximale Poolgröße des Objektpoolings erreicht ist. Daher werden u. U. einige Anforderungen zur Objekterstellung nicht erfüllt, indem eine Timeoutausnahme ausgelöst wird.

Hinweis

Die Einrichtungsverfahren und Build-Anweisungen für dieses Beispiel befinden sich am Ende dieses Themas.

Der erste Schritt beim Erstellen einer WCF-Erweiterung besteht darin, den zu verwendenden Erweiterbarkeitspunkt zu entscheiden.

In WCF bezieht sich der Begriff Dispatcher auf eine Laufzeitkomponente, die verantwortlich ist für die Umwandlung eingehender Nachrichten in Methodenaufrufe im Benutzerdienst und die Konvertierung von Rückgabewerten dieser Methode in eine ausgehende Nachricht. Ein WCF-Dienst erstellt einen Verteiler für jeden Endpunkt. Ein WCF-Client muss einen Verteiler verwenden, wenn der diesem Client zugeordnete Vertrag ein Duplex-Vertrag ist.

Die Kanal- und Endpunktverteiler bieten kanal- und vertragsweite Erweiterbarkeit, indem verschiedene Eigenschaften verfügbar sind, die das Verhalten des Verteilers steuern. Mit der DispatchRuntime Eigenschaft können Sie auch den Verteilerprozess überprüfen, ändern oder anpassen. Dieses Beispiel konzentriert sich auf die InstanceProvider Eigenschaft, die auf das Objekt verweist, das die Instanzen der Dienstklasse bereitstellt.

Der IInstanceProvider

In WCF erstellt der Dispatcher Instanzen der Dienstklasse mithilfe eines InstanceProvider, das die IInstanceProvider Schnittstelle implementiert. Diese Schnittstelle verfügt über drei Methoden:

Der Objektpool

Eine individuelle IInstanceProvider Implementierung liefert die erforderliche Objekt-Pooling-Semantik für einen Dienst. Daher verfügt dieses Beispiel über einen ObjectPoolingInstanceProvider Typ, der eine benutzerdefinierte Implementierung von IInstanceProvider für das Pooling bereitstellt. Wenn die DispatcherGetInstance(InstanceContext, Message) Methode aufgerufen wird, anstatt eine neue Instanz zu erstellen, sucht die benutzerdefinierte Implementierung nach einem vorhandenen Objekt in einem Speicherpool. Wenn eine verfügbar ist, wird sie zurückgegeben. Andernfalls wird ein neues Objekt erstellt. Die Implementierung für GetInstance wird im folgenden Beispielcode gezeigt.

object IInstanceProvider.GetInstance(InstanceContext instanceContext, Message message)
{
    object obj = null;

    lock (poolLock)
    {
        if (pool.Count > 0)
        {
            obj = pool.Pop();
        }
        else
        {
            obj = CreateNewPoolObject();
        }
        activeObjectsCount++;
    }

    WritePoolMessage(ResourceHelper.GetString("MsgNewObject"));

    idleTimer.Stop();

    return obj;
}

Die benutzerdefinierte ReleaseInstance Implementierung fügt die freigegebene Instanz wieder dem Pool hinzu und verringert den ActiveObjectsCount Wert. Die Dispatcher Methoden können von verschiedenen Threads aufgerufen werden, und daher ist der synchronisierte Zugriff auf die Elemente auf Klassenebene in der ObjectPoolingInstanceProvider Klasse erforderlich.

void IInstanceProvider.ReleaseInstance(InstanceContext instanceContext, object instance)
{
    lock (poolLock)
    {
        pool.Push(instance);
        activeObjectsCount--;

        WritePoolMessage(
        ResourceHelper.GetString("MsgObjectPooled"));

        // When the service goes completely idle (no requests
        // are being processed), the idle timer is started
        if (activeObjectsCount == 0)
            idleTimer.Start();
    }
}

Die ReleaseInstance-Methode stellt eine Funktion zur „Bereinigungsinitialisierung“ bereit. Normalerweise wird im Pool eine Mindestanzahl von Objekten für die Lebensdauer des Pools beibehalten. Es kann jedoch Zeiträume mit übermäßiger Nutzung geben, die das Erstellen zusätzlicher Objekte im Pool erfordern, um die in der Konfiguration angegebene Höchstgrenze zu erreichen. Wenn der Pool schließlich weniger aktiv wird, können diese überschüssigen Objekte zu einem zusätzlichen Mehraufwand werden. Wenn activeObjectsCount daher 0 (null) erreicht, wird ein Leerlauftimer gestartet, der einen Bereinigungszyklus auslöst und ausführt.

Hinzufügen des Verhaltens

Dispatcher-Layer-Erweiterungen werden mithilfe der folgenden Verhaltensweisen eingebunden:

  • Dienstverhalten. Diese ermöglichen die Anpassung der gesamten Dienstlaufzeit.

  • Endpunktverhalten. Diese ermöglichen die Anpassung von Dienstendpunkten, insbesondere eines Kanals und eines Endpunkt-Dispatchers.

  • Vertragsverhalten. Diese ermöglichen die Anpassung von beiden ClientRuntime und DispatchRuntime Klassen auf dem Client und dem Dienst.

Für den Zweck einer Objektpoolerweiterung muss ein Dienstverhalten erstellt werden. Dienstverhalten wird durch Implementieren der IServiceBehavior Schnittstelle erstellt. Es gibt verschiedene Möglichkeiten, das Dienstmodell auf die benutzerdefinierten Verhaltensweisen aufmerksam zu machen:

  • Verwenden eines benutzerdefinierten Attributs.

  • Dieses wird der Verhaltensauflistung der Dienstbeschreibung imperativ hinzugefügt.

  • Erweitern der Konfigurationsdatei.

In diesem Beispiel wird ein benutzerdefiniertes Attribut verwendet. Wenn ServiceHost konstruiert wird, untersucht es die Attribute, die in der Typdefinition des Dienstes verwendet werden, und fügt die verfügbaren Verhaltensweisen der Verhaltenssammlung der Dienstbeschreibung hinzu.

Die Schnittstelle IServiceBehavior verfügt über drei Methoden darin : Validate- , , AddBindingParametersund ApplyDispatchBehavior. Die Validate Methode wird verwendet, um sicherzustellen, dass das Verhalten auf den Dienst angewendet werden kann. In diesem Beispiel stellt die Implementierung sicher, dass der Service nicht mit Single konfiguriert ist. Die AddBindingParameters Methode wird verwendet, um die Bindungen des Diensts zu konfigurieren. Es ist in diesem Szenario nicht erforderlich. Dies ApplyDispatchBehavior wird verwendet, um die Verteiler des Diensts zu konfigurieren. Diese Methode wird von WCF aufgerufen, wenn die ServiceHost initialisiert wird. Die folgenden Parameter werden an diese Methode übergeben:

  • Description: Dieses Argument stellt die Dienstbeschreibung für den gesamten Dienst bereit. Dies kann verwendet werden, um Beschreibungsdaten zu den Endpunkten, Verträgen, Bindungen und anderen Daten des Diensts zu prüfen.

  • ServiceHostBase: Dieses Argument stellt das ServiceHostBase bereit, das aktuell initialisiert wird.

In der benutzerdefinierten IServiceBehavior Implementierung wird eine neue Instanz von ObjectPoolingInstanceProvider instanziiert und der InstanceProvider Eigenschaft in jeder DispatchRuntime in ServiceHostBase zugewiesen.

void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
    // Create an instance of the ObjectPoolInstanceProvider.
    ObjectPoolingInstanceProvider instanceProvider = new
           ObjectPoolingInstanceProvider(description.ServiceType,
                                                    minPoolSize);

    // Forward the call if we created a ServiceThrottlingBehavior.
    if (this.throttlingBehavior != null)
    {
        ((IServiceBehavior)this.throttlingBehavior).ApplyDispatchBehavior(description, serviceHostBase);
    }

    // In case there was already a ServiceThrottlingBehavior
    // (this.throttlingBehavior==null), it should have initialized
    // a single ServiceThrottle on all ChannelDispatchers.
    // As we loop through the ChannelDispatchers, we verify that
    // and modify the ServiceThrottle to guard MaxPoolSize.
    ServiceThrottle throttle = null;

    foreach (ChannelDispatcherBase cdb in
            serviceHostBase.ChannelDispatchers)
    {
        ChannelDispatcher cd = cdb as ChannelDispatcher;
        if (cd != null)
        {
            // Make sure there is exactly one throttle used by all
            // endpoints. If there were others, we could not enforce
            // MaxPoolSize.
            if ((this.throttlingBehavior == null) &&
                        (this.maxPoolSize != Int32.MaxValue))
            {
                throttle ??= cd.ServiceThrottle;
                if (cd.ServiceThrottle == null)
                {
                    throw new
InvalidOperationException(ResourceHelper.GetString("ExNullThrottle"));
                }
                if (throttle != cd.ServiceThrottle)
                {
                    throw new InvalidOperationException(ResourceHelper.GetString("ExDifferentThrottle"));
                }
             }

             foreach (EndpointDispatcher ed in cd.Endpoints)
             {
                 // Assign it to DispatchBehavior in each endpoint.
                 ed.DispatchRuntime.InstanceProvider =
                                      instanceProvider;
             }
         }
     }

     // Set the MaxConcurrentInstances to limit the number of items
     // that will ever be requested from the pool.
     if ((throttle != null) && (throttle.MaxConcurrentInstances >
                                      this.maxPoolSize))
     {
         throttle.MaxConcurrentInstances = this.maxPoolSize;
     }
}

Zusätzlich zu einer IServiceBehavior Implementierung verfügt die ObjectPoolingAttribute Klasse über mehrere Member, um den Objektpool mithilfe der Attributargumente anzupassen. Diese Members umfassen MaxPoolSize, MinPoolSize und CreationTimeout für die Übereinstimmung mit dem von .NET Enterprise Services bereitgestellten Objektpooling-Funktionssatz.

Das Objektpoolverhalten kann nun einem WCF-Dienst hinzugefügt werden, indem die Dienstimplementierung mit dem neu erstellten benutzerdefinierten ObjectPooling Attribut kommentiert wird.

[ObjectPooling(MaxPoolSize=1024, MinPoolSize=10, CreationTimeout=30000)]
public class PoolService : IPoolService
{
  // …
}

Ausführen des Beispiels

Das Beispiel veranschaulicht die Leistungsvorteile, die durch die Verwendung von Objektpools in bestimmten Szenarien erzielt werden können.

Die Dienstanwendung implementiert zwei Dienste : WorkService und ObjectPooledWorkService. Beide Dienste teilen sich dieselbe Implementierung - beide erfordern eine teure Initialisierung und machen dann eine DoWork() Methode verfügbar, die relativ billig ist. Der einzige Unterschied besteht darin, dass ObjectPooledWorkService mit Objektpooling konfiguriert ist.

[ObjectPooling(MinPoolSize = 0, MaxPoolSize = 5)]
public class ObjectPooledWorkService : IDoWork
{
    public ObjectPooledWorkService()
    {
        Thread.Sleep(5000);
        ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService instance created.");
    }

    public void DoWork()
    {
        ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService.GetData() completed.");
    }
}

Wenn Sie den Client ausführen, stoppt er die Zeit, die benötigt wird, um WorkService 5-mal aufzurufen. Er stoppt dann die Zeit, die benötigt wird, um ObjectPooledWorkService 5-mal aufzurufen. Der Zeitunterschied wird dann angezeigt:

Press <ENTER> to start the client.

Calling WorkService:
1 - DoWork() Done
2 - DoWork() Done
3 - DoWork() Done
4 - DoWork() Done
5 - DoWork() Done
Calling WorkService took: 26722 ms.
Calling ObjectPooledWorkService:
1 - DoWork() Done
2 - DoWork() Done
3 - DoWork() Done
4 - DoWork() Done
5 - DoWork() Done
Calling ObjectPooledWorkService took: 5323 ms.
Press <ENTER> to exit.

Hinweis

Wenn der Client zum ersten Mal ausgeführt wird, scheinen beide Dienste ungefähr dieselbe Zeit in Anspruch zu nehmen. Wenn Sie das Beispiel erneut ausführen, können Sie sehen, dass die ObjectPooledWorkService Rückgabe viel schneller zurückgegeben wird, da eine Instanz dieses Objekts bereits im Pool vorhanden ist.

So können Sie das Beispiel einrichten, erstellen und ausführen

  1. Stellen Sie sicher, dass Sie das One-Time Setup-Verfahren für die Windows Communication Foundation-Beispieleausgeführt haben.

  2. Befolgen Sie zum Erstellen der Projektmappe die Anweisungen unter Erstellen der Windows Communication Foundation-Beispiele.

  3. Wenn Sie das Beispiel in einer Konfiguration mit einem Computer oder über Computer hinweg ausführen möchten, folgen Sie den Anweisungen unter Durchführen der Windows Communication Foundation-Beispiele.

Hinweis

Wenn Sie Svcutil.exe verwenden, um die Konfiguration für dieses Beispiel neu zu generieren, müssen Sie den Endpunktnamen in der Clientkonfiguration so ändern, dass er mit dem Clientcode übereinstimmt.