Share via


Initialisierung der Instanziierung

In diesem Beispiel wird das Pooling-Beispiel erweitert, indem die Schnittstelle IObjectControl definiert wird, die die Initialisierung eines Objekts durch Aktivieren und Deaktivieren anpasst. Der Client ruft Methoden auf, die das Objekt an den Pool zurückgeben und das Objekt nicht an den Pool zurückgeben.

Tipp

Die Setupprozedur und die Buildanweisungen für dieses Beispiel befinden sich am Ende dieses Themas.

Erweiterungspunkte

Der erste Schritt beim Erstellen einer Windows Communication Foundation (WCF)-Erweiterung besteht darin, den zu verwendenden Erweiterungspunkt auszuwählen. In WCF bezeichnet EndpointDispatcher eine Laufzeitkomponente, die eingehende Nachrichten in Methodenaufrufe für den Dienst des Benutzers konvertiert und Rückgabewerte von dieser Methode in eine ausgehende Nachricht konvertiert. Ein WCF-Dienst erstellt einen EndpointDispatcher für jeden Endpunkt.

Der EndpointDispatcher stellt mithilfe der EndpointDispatcher-Klasse die Erweiterung des Endpunktbereichs bereit (für alle vom Dienst empfangenen oder gesendeten Nachrichten). Mit dieser Klasse können Sie verschiedene Eigenschaften anpassen, die das Verhalten von EndpointDispatcher steuern. In diesem Beispiel wird in erster Linie die InstanceProvider-Eigenschaft behandelt, die auf das Objekt zeigt, das die Instanzen der Dienstklasse bereitstellt.

IInstanceProvider

In WCF erstellt EndpointDispatcher mithilfe eines Instanzenanbieters, der die IInstanceProvider-Schnittstelle implementiert, Instanzen einer Dienstklasse. Diese Schnittstelle verfügt über nur zwei Methoden:

  • GetInstance: Wenn eine Nachricht eingeht, ruft der Verteiler die GetInstance-Methode auf, um eine Instanz der Dienstklasse zum Verarbeiten der Nachricht zu erstellen. Die Häufigkeit der Aufrufe dieser Methode wird von der InstanceContextMode-Eigenschaft bestimmt. Wenn die InstanceContextMode-Eigenschaft beispielsweise auf System.ServiceModel.InstanceContextMode.PerCall festgelegt ist, wird eine neue Instanz der Dienstklasse erstellt, um alle eingehenden Nachrichten zu verarbeiten. Daher wird GetInstance immer dann aufgerufen, wenn eine Nachricht eingeht.
  • ReleaseInstance: Wenn die Dienstinstanz das Verarbeiten der Nachricht abgeschlossen hat, ruft EndpointDispatcher die ReleaseInstance-Methode auf. Wie bei der GetInstance-Methode wird die Häufigkeit der Aufrufe dieser Methode von der InstanceContextMode-Eigenschaft bestimmt.

Der Objektpool

Die ObjectPoolInstanceProvider-Klasse enthält die Implementierung des Objektpools. Diese Klasse implementiert die IInstanceProvider-Schnittstelle für die Interaktion mit der Dienstmodellebene. Wenn EndpointDispatcher die GetInstance-Methode aufruft, erstellt die benutzerdefinierte Implementierung keine neue Instanz, sondern sucht ein vorhandenes Objekt in einem Pool im Speicher. Wenn ein Objekt verfügbar ist, wird es zurückgegeben. Andernfalls überprüft ObjectPoolInstanceProvider, ob die ActiveObjectsCount-Eigenschaft (Anzahl der aus dem Pool zurückgegebenen Objekte) die maximale Poolgröße erreicht hat. Wenn dies nicht der Fall ist, wird eine neue Instanz erstellt und an den Aufrufer zurückgegeben, und anschließend wird ActiveObjectsCount inkrementiert. Andernfalls wird eine Objekterstellungsanforderung für einen konfigurierten Zeitraum in die Warteschlange gestellt. Die Implementierung für GetObjectFromThePool wird im folgenden Beispielcode dargestellt.

private object GetObjectFromThePool()
{
    bool didNotTimeout = 
       availableCount.WaitOne(creationTimeout, true);
    if(didNotTimeout)
    {
         object obj = null;
         lock (poolLock)
        {
             if (pool.Count != 0)
             {
                   obj = pool.Pop();
                   activeObjectsCount++;
             }
             else if (pool.Count == 0)
             {
                   if (activeObjectsCount < maxPoolSize)
                   {
                        obj = CreateNewPoolObject();
                        activeObjectsCount++;
                            
                        #if (DEBUG)
                        WritePoolMessage(
                             ResourceHelper.GetString("MsgNewObject"));
                       #endif
                   }                        
            }
           idleTimer.Stop();
      }
     // Call the Activate method if possible.
    if (obj is IObjectControl)
   {
         ((IObjectControl)obj).Activate();
   }
   return obj;
}
throw new TimeoutException(
ResourceHelper.GetString("ExObjectCreationTimeout"));
}

Die benutzerdefinierte ReleaseInstance-Implementierung fügt die freigegebene Instanz dem Pool erneut hinzu und dekrementiert den ActiveObjectsCount-Wert. EndpointDispatcher kann diese Methoden von unterschiedlichen Threads aus aufrufen. Daher ist ein synchronisierter Zugriff auf die Member der Klassenebene in der ObjectPoolInstanceProvider-Klasse erforderlich.

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            lock (poolLock)
            {
                // Check whether the object can be pooled. 
                // Call the Deactivate method if possible.
                if (instance is IObjectControl)
                {
                    IObjectControl objectControl = (IObjectControl)instance;
                    objectControl.Deactivate();

                    if (objectControl.CanBePooled)
                    {
                        pool.Push(instance);

                        #if(DEBUG)
                        WritePoolMessage(
                            ResourceHelper.GetString("MsgObjectPooled"));
                        #endif                        
                    }
                    else
                    {
                        #if(DEBUG)
                        WritePoolMessage(
                            ResourceHelper.GetString("MsgObjectWasNotPooled"));
                        #endif
                    }
                }
                else
                {
                    pool.Push(instance);

                    #if(DEBUG)
                    WritePoolMessage(
                        ResourceHelper.GetString("MsgObjectPooled"));
                    #endif 
                }
                                
                activeObjectsCount--;

                if (activeObjectsCount == 0)
                {
                    idleTimer.Start();                     
                }
            }

            availableCount.Release(1);
        }

Die ReleaseInstance-Methode stellt ein Feature zur Bereinigungsinitialisierung bereit. Normalerweise wird im Pool eine Mindestanzahl von Objekten für die Lebensdauer des Pools beibehalten. Es kann jedoch Zeiten mit übermäßiger Auslastung geben, für die im Pool zusätzliche Objekte erstellt werden müssen, um die in der Konfiguration festgelegte Höchstgrenze zu erreichen. Wenn der Pool weniger aktiv ist, stellen diese überzähligen Objekte einen zusätzlichen Aufwand dar. Wenn activeObjectsCount daher 0 (null) erreicht, wird ein Leerlaufzeitgeber gestartet, der einen Bereinigungszyklus auslöst und ausführt.

if (activeObjectsCount == 0)
{
    idleTimer.Start(); 
}

ServiceModel-Ebenenerweiterungen werden mithilfe der folgenden Verhalten verknüpft:

  • Dienstverhalten: Diese ermöglichen die Anpassung der ganzen Dienstlaufzeit.
  • Endpunktverhalten: Diese ermöglichen das Anpassen eines bestimmten Dienstendpunkts, einschließlich EndpointDispatcher.
  • Vertragsverhalten: Diese ermöglichen das Anpassen von ClientRuntime-Klassen oder DispatchRuntime-Klassen auf dem Client bzw. Server.
  • Vorgangsverhalten: Diese ermöglichen das Anpassen von ClientOperation-Klassen oder DispatchOperation-Klassen auf dem Client bzw. Server.

Für den Zweck einer Objektpoolingerweiterung kann ein Endpunktverhalten oder ein Dienstverhalten erstellt werden. In diesem Beispiel wird ein Dienstverhalten verwendet, das die Objektpoolingfähigkeit auf alle Endpunkte des Diensts anwendet. Dienstverhaltensweisen werden durch Implementieren der IServiceBehavior-Schnittstelle erstellt. Es gibt mehrere Möglichkeiten, ServiceModel auf die benutzerdefinierten Verhaltensweisen hinzuweisen:

  • Verwenden eines benutzerdefinierten Attributs.
  • Imperatives Hinzufügen zur Verhaltensauflistung der Dienstbeschreibung.
  • Erweitern der Konfigurationsdatei.

In diesem Beispiel wird ein benutzerdefiniertes Attribut verwendet. Beim Erstellen von ServiceHost werden die in der Typdefinition des Diensts verwendeten Attribute untersucht, und die verfügbaren Verhalten werden der Verhaltensauflistung der Dienstbeschreibung hinzugefügt.

Die IServiceBehavior-Schnittstelle verfügt über drei Methoden: Validate, AddBindingParameters, und ApplyDispatchBehavior. Diese Methoden werden von WCF aufgerufen, wenn ServiceHost initialisiert wird. System.ServiceModel.Description.IServiceBehavior.Validate(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase) wird zuerst aufgerufen. So kann der Dienst auf Inkonsistenzen untersucht werden. System.ServiceModel.Description.IServiceBehavior.AddBindingParameters(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase,System.Collections.ObjectModel.Collection{System.ServiceModel.Description.ServiceEndpoint},System.ServiceModel.Channels.BindingParameterCollection) wird danach aufgerufen. Diese Methode ist nur in sehr komplexen Szenarios erforderlich. System.ServiceModel.Description.IServiceBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase) wird zuletzt aufgerufen und ist für das Konfigurieren der Laufzeit zuständig. Die folgenden Parameter werden in System.ServiceModel.Description.IServiceBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase) übergeben:

  • Description: Dieser Parameter stellt die Dienstbeschreibung für den gesamten Dienst bereit. Dies kann verwendet werden, um Beschreibungsdaten über die Endpunkte, Verträge, Bindungen und andere Daten zu dem Dienst zu überprüfen.
  • ServiceHostBase: Dieser Parameter stellt die ServiceHostBase bereit, die gerade initialisiert wird.

In der benutzerdefinierten IServiceBehavior-Implementierung wird eine neue Instanz von ObjectPoolInstanceProvider instanziiert und der InstanceProvider-Eigenschaft in jedem EndpointDispatcher zugewiesen, der ServiceHostBase angefügt ist.

public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
    if (enabled)
    {
        // Create an instance of the ObjectPoolInstanceProvider.
        instanceProvider = new ObjectPoolInstanceProvider(description.ServiceType,
        maxPoolSize, minPoolSize, creationTimeout);

        // Assign our instance provider to Dispatch behavior in each 
        // endpoint.
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
             ChannelDispatcher cd = cdb as ChannelDispatcher;
             if (cd != null)
             {
                 foreach (EndpointDispatcher ed in cd.Endpoints)
                 {
                        ed.DispatchRuntime.InstanceProvider = instanceProvider;
                 }
             }
         }
     }
} 

Neben einer IServiceBehavior-Implementierung verfügt die ObjectPoolingAttribute-Klasse über mehrere Member zum Anpassen des Objektpools mithilfe der Attributargumente. Diese Member umfassen MaxSize, MinSize, Enabled und CreationTimeout für die Übereinstimmung mit dem von .NET Enterprise Services bereitgestellten Objektpooling-Featuresatz.

Das Objektpoolingverhalten kann nun einem WCF-Dienst hinzugefügt werden, indem der Dienstimplementierung mit dem neu erstellten, benutzerdefinierten ObjectPooling-Attribut eine Anmerkung hinzugefügt wird.

[ObjectPooling(MaxSize=1024, MinSize=10, CreationTimeout=30000]    
public class PoolService : IPoolService
{
  // …
}

Aktivieren und Deaktivieren von Verknüpfungen

Das primäre Ziel des Objektpoolings ist das Optimieren von Objekten mit kurzer Lebensdauer und relativ aufwändiger Erstellung und Initialisierung. Bei der richtigen Verwendung kann daher eine erhebliche Leistungssteigerung erreicht werden. Da das Objekt aus dem Pool zurückgegeben wird, wird der Konstruktor nur einmal aufgerufen. Bei einigen Anwendungen ist jedoch eine gewisse Kontrolle erforderlich, damit sie die in einem einzigen Kontext verwendeten Ressourcen initialisieren und bereinigen können. Ein Objekt, das beispielsweise für eine Gruppe von Berechnungen verwendet wird, kann die privaten Felder zurücksetzen, bevor die nächste Berechnung verarbeitet wird. In Enterprise Services wurde diese Art der kontextspezifischen Initialisierung ermöglicht, indem der Objektentwickler die Activate-Methode und die Deactivate-Methode in der ServicedComponent-Basisklasse überschreiben konnte.

Der Objektpool ruft die Activate-Methode auf, kurz bevor das Objekt aus dem Pool zurückgegeben wird. Deactivate wird aufgerufen, wenn das Objekt zum Pool zurückgegeben wird. Die ServicedComponent-Basisklasse verfügt außerdem über die boolean-Eigenschaft mit der Bezeichnung CanBePooled, mit der der Pool darüber benachrichtigt werden kann, ob das Objekt weiter gepoolt werden kann.

Im Beispiel wird eine öffentliche Schnittstelle (IObjectControl) deklariert, die über die oben genannten Member verfügt, um diese Funktionalität zu imitieren. Diese Schnittstelle wird dann von Dienstklassen implementiert, die die kontextspezifische Initialisierung bereitstellen sollen. Die IInstanceProvider-Implementierung muss geändert werden, um diese Anforderungen zu erfüllen. Wenn Sie nun ein Objekt durch Aufrufen der GetInstance-Methode abrufen, müssen Sie jedes Mal überprüfen, ob das Objekt IObjectControl implementiert. Wenn dies der Fall ist, müssen Sie die Activate-Methode entsprechend aufrufen.

if (obj is IObjectControl)
{
    ((IObjectControl)obj).Activate();
}

Beim Zurückgeben eines Objekts an den Pool ist eine Überprüfung für die CanBePooled-Eigenschaft erforderlich, bevor das Objekt erneut dem Pool hinzugefügt wird.

if (instance is IObjectControl)
{
    IObjectControl objectControl = (IObjectControl)instance;
    objectControl.Deactivate();
    if (objectControl.CanBePooled)
    {
       pool.Push(instance);
    }
}

Da der Dienstentwickler entscheiden kann, ob ein Objekt gepoolt werden kann, kann die Objektanzahl im Pool zu einem bestimmten Zeitpunkt unter dem Mindestwert liegen. Sie müssen daher überprüfen, ob die Objektanzahl unter dem Mindestwert liegt, und die erforderliche Initialisierung in der Bereinigungsprozedur ausführen.

// Remove the surplus objects.
if (pool.Count > minPoolSize)
{
  // Clean the surplus objects.
}                    
else if (pool.Count < minPoolSize)
{
  // Reinitialize the missing objects.
  while(pool.Count != minPoolSize)
  {
    pool.Push(CreateNewPoolObject());
  }
}

Wenn Sie das Beispiel ausführen, werden die Anforderungen und Antworten für den Vorgang im Dienst- und Clientkonsolenfenster angezeigt. Drücken Sie die EINGABETASTE in den einzelnen Konsolenfenstern, um den Dienst und den Client zu schließen.

So richten Sie das Beispiel ein, erstellen es und führen es aus

  1. Stellen Sie sicher, dass Sie Beispiele zum einmaligen Setupverfahren für Windows Communication Foundation ausgefü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, befolgen Sie die Anweisungen unter Durchführen der Windows Communication Foundation-Beispiele.

Send comments about this topic to Microsoft.
© 2007 Microsoft Corporation. All rights reserved.