Share via


In een groep plaatsen

Het poolvoorbeeld laat zien hoe u WCF (Windows Communication Foundation) kunt uitbreiden ter ondersteuning van objectpooling. In het voorbeeld ziet u hoe u een kenmerk maakt dat syntactisch en semantisch vergelijkbaar is met de ObjectPoolingAttribute kenmerkfunctionaliteit van Enterprise Services. Objectpooling kan de prestaties van een toepassing aanzienlijk verbeteren. Het kan echter het tegenovergestelde effect hebben als het niet goed wordt gebruikt. Objectpooling vermindert de overhead van het opnieuw maken van veelgebruikte objecten waarvoor uitgebreide initialisatie is vereist. Als het echter veel tijd kost om een aanroep naar een methode op een gegroepeerd object te voltooien, worden extra aanvragen bij objectpooling in wachtrijen geplaatst zodra de maximale poolgrootte is bereikt. Het kan dus mislukken om bepaalde aanvragen voor het maken van objecten te verwerken door een time-outuitzondering te genereren.

Notitie

De installatieprocedure en build-instructies voor dit voorbeeld bevinden zich aan het einde van dit onderwerp.

De eerste stap bij het maken van een WCF-extensie is het bepalen van het uitbreidbaarheidspunt dat moet worden gebruikt.

In WCF verwijst de term dispatcher naar een runtimeonderdeel dat verantwoordelijk is voor het converteren van binnenkomende berichten naar methodeaanroepen op de service van de gebruiker en voor het converteren van retourwaarden van die methode naar een uitgaand bericht. Een WCF-service maakt een dispatcher voor elk eindpunt. Een WCF-client moet een dispatcher gebruiken als het contract dat aan die klant is gekoppeld een dubbelzijdig contract is.

De kanaal- en eindpuntverzenders bieden kanaal- en contractbreidbaarheid door verschillende eigenschappen weer te geven waarmee het gedrag van de dispatcher wordt bepaald. Met DispatchRuntime de eigenschap kunt u ook het verzendingsproces inspecteren, wijzigen of aanpassen. Dit voorbeeld is gericht op de InstanceProvider eigenschap die verwijst naar het object dat de exemplaren van de serviceklasse levert.

De IInstanceProvider

In WCF maakt de dispatcher exemplaren van de serviceklasse met behulp van een InstanceProvider, waarmee de IInstanceProvider interface wordt geïmplementeerd. Deze interface heeft drie methoden:

De objectgroep

Een aangepaste IInstanceProvider implementatie biedt de vereiste semantiek voor objectpooling voor een service. Daarom heeft dit voorbeeld een ObjectPoolingInstanceProvider type dat aangepaste implementatie van IInstanceProvider pooling biedt. Wanneer de DispatcherGetInstance(InstanceContext, Message) methode wordt aangeroepen in plaats van een nieuw exemplaar te maken, zoekt de aangepaste implementatie naar een bestaand object in een in-memory pool. Als er een beschikbaar is, wordt deze geretourneerd. Anders wordt er een nieuw object gemaakt. De implementatie voor GetInstance wordt weergegeven in de volgende voorbeeldcode.

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

Met de aangepaste implementatie wordt het uitgebrachte ReleaseInstance exemplaar weer toegevoegd aan de pool en wordt de ActiveObjectsCount waarde gedegraded. Deze Dispatcher methoden kunnen worden aangeroepen vanuit verschillende threads en daarom is gesynchroniseerde toegang tot de leden op klasseniveau in de ObjectPoolingInstanceProvider klasse vereist.

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

De ReleaseInstance methode biedt een functie voor het opschonen van initialisatie. Normaal gesproken onderhoudt de pool een minimum aantal objecten voor de levensduur van de pool. Er kunnen echter perioden zijn van overmatig gebruik waarvoor het maken van extra objecten in de groep nodig is om de maximale limiet te bereiken die is opgegeven in de configuratie. Uiteindelijk, wanneer de pool minder actief wordt, kunnen deze overschotobjecten een extra overhead worden. Wanneer de activeObjectsCount nul bereikt, wordt daarom een niet-actieve timer gestart die wordt geactiveerd en een opschooncyclus wordt uitgevoerd.

Het gedrag toevoegen

Extensies voor dispatcherlagen worden gekoppeld met behulp van het volgende gedrag:

  • Servicegedrag. Hierdoor kan de volledige serviceruntime worden aangepast.

  • Eindpuntgedrag. Hiermee kunt u service-eindpunten aanpassen, met name een kanaal- en eindpunt-dispatcher.

  • Contractgedrag. Hierdoor kunnen zowel ClientRuntimeDispatchRuntime als klassen op de client en de service worden aangepast.

Voor het doel van een objectgroeperingsextensie moet een servicegedrag worden gemaakt. Servicegedrag wordt gemaakt door de IServiceBehavior interface te implementeren. Er zijn verschillende manieren om het servicemodel bewust te maken van het aangepaste gedrag:

  • Een aangepast kenmerk gebruiken.

  • Imperatief toevoegen aan de gedragverzameling van de servicebeschrijving.

  • Het configuratiebestand uitbreiden.

In dit voorbeeld wordt een aangepast kenmerk gebruikt. Wanneer de ServiceHost service is samengesteld, worden de kenmerken onderzocht die worden gebruikt in de typedefinitie van de service en worden de beschikbare gedragingen toegevoegd aan de verzameling gedrag van de servicebeschrijving.

De interface IServiceBehavior heeft drie methoden : - Validate, AddBindingParametersen ApplyDispatchBehavior. De Validate methode wordt gebruikt om ervoor te zorgen dat het gedrag op de service kan worden toegepast. In dit voorbeeld zorgt de implementatie ervoor dat de service niet is geconfigureerd met Single. De AddBindingParameters methode wordt gebruikt om de bindingen van de service te configureren. Dit is niet vereist in dit scenario. Deze ApplyDispatchBehavior wordt gebruikt om de dispatchers van de service te configureren. Deze methode wordt aangeroepen door WCF wanneer de ServiceHost wordt geïnitialiseerd. De volgende parameters worden doorgegeven aan deze methode:

  • Description: Dit argument bevat de servicebeschrijving voor de hele service. Dit kan worden gebruikt om beschrijvingsgegevens over de eindpunten, contracten, bindingen en andere gegevens van de service te controleren.

  • ServiceHostBase: Dit argument bevat het ServiceHostBase argument dat momenteel wordt geïnitialiseerd.

In de aangepaste IServiceBehavior implementatie wordt een nieuw exemplaar van ObjectPoolingInstanceProvider geïnstantieerd en toegewezen aan de InstanceProvider eigenschap in elk DispatchRuntime in de ServiceHostBase.

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

Naast een IServiceBehavior implementatie heeft de ObjectPoolingAttribute klasse verschillende leden om de objectgroep aan te passen met behulp van de kenmerkargumenten. Deze leden omvatten MaxPoolSize, MinPoolSizeen CreationTimeout, die overeenkomen met de functieset voor objectpooling die wordt geleverd door .NET Enterprise Services.

Het gedrag van objectpooling kan nu worden toegevoegd aan een WCF-service door aantekeningen te maken bij de service-implementatie met het zojuist gemaakte aangepaste ObjectPooling kenmerk.

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

Het voorbeeld uitvoeren

In het voorbeeld ziet u de prestatievoordelen die kunnen worden verkregen met behulp van objectpooling in bepaalde scenario's.

De servicetoepassing implementeert twee services , WorkService en ObjectPooledWorkService. Beide services delen dezelfde implementatie. Ze vereisen allebei dure initialisatie en maken vervolgens een DoWork() methode beschikbaar die relatief goedkoop is. Het enige verschil is dat objectpooling ObjectPooledWorkService is geconfigureerd:

[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.");
    }
}

Wanneer u de client uitvoert, wordt de WorkService 5 keer aangeroepen. Vervolgens wordt de ObjectPooledWorkService 5 keer gebeld. Het verschil in tijd wordt vervolgens weergegeven:

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.

Notitie

De eerste keer dat de client wordt uitgevoerd, lijken beide services ongeveer dezelfde tijd in beslag te nemen. Als u het voorbeeld opnieuw uitvoert, kunt u zien dat de ObjectPooledWorkService retourneert veel sneller omdat er al een exemplaar van dat object in de pool bestaat.

Het voorbeeld instellen, compileren en uitvoeren

  1. Zorg ervoor dat u de eenmalige installatieprocedure voor de Windows Communication Foundation-voorbeelden hebt uitgevoerd.

  2. Volg de instructies in Het bouwen van de Windows Communication Foundation-voorbeelden om de oplossing te bouwen.

  3. Als u het voorbeeld wilt uitvoeren in een configuratie met één of meerdere computers, volgt u de instructies in Het uitvoeren van de Windows Communication Foundation-voorbeelden.

Notitie

Als u Svcutil.exe gebruikt om de configuratie voor dit voorbeeld opnieuw te genereren, moet u de naam van het eindpunt in de clientconfiguratie wijzigen zodat deze overeenkomt met de clientcode.