Instancing-initiering

Initieringsexemplet utökar poolexemplet genom att definiera ett gränssnitt, IObjectControl, som anpassar initieringen av ett objekt genom att aktivera och inaktivera det. Klienten anropar metoder som returnerar objektet till poolen och som inte returnerar objektet till poolen.

Kommentar

Installationsproceduren och bygginstruktionerna för det här exemplet finns i slutet av det här avsnittet.

Utökningspunkter

Det första steget i att skapa ett WCF-tillägg (Windows Communication Foundation) är att bestämma utökningspunkten som ska användas. I WCF refererar termen EndpointDispatcher till en körningskomponent som ansvarar för att konvertera inkommande meddelanden till metodanrop i användarens tjänst och för att konvertera returvärden från den metoden till ett utgående meddelande. En WCF-tjänst skapar en EndpointDispatcher för varje slutpunkt.

EndpointDispatcher erbjuder slutpunktsomfång (för alla meddelanden som tas emot eller skickas av tjänsten) utökningsbarhet med hjälp av EndpointDispatcher klassen. Med den här klassen kan du anpassa olika egenskaper som styr beteendet för EndpointDispatcher. Det här exemplet fokuserar på egenskapen InstanceProvider som pekar på objektet som tillhandahåller instanserna av tjänstklassen.

IInstanceProvider

I WCF skapar EndpointDispatcher instanser av en tjänstklass med hjälp av en instansprovider som implementerar IInstanceProvider gränssnittet. Det här gränssnittet har bara två metoder:

Objektpoolen

Klassen ObjectPoolInstanceProvider innehåller implementeringen för objektpoolen. Den här klassen implementerar IInstanceProvider gränssnittet för att interagera med tjänstmodelllagret. När EndpointDispatcher anropar GetInstance metoden söker den anpassade implementeringen efter ett befintligt objekt i en minnesintern pool i stället för att skapa en ny instans. Om en är tillgänglig returneras den. Annars ObjectPoolInstanceProvider kontrollerar om ActiveObjectsCount egenskapen (antalet objekt som returneras från poolen) har nått den maximala poolstorleken. Annars skapas och returneras en ny instans till anroparen och ActiveObjectsCount ökas därefter. Annars placeras en begäran om att skapa objekt i kö under en konfigurerad tidsperiod. Implementeringen för GetObjectFromThePool visas i följande exempelkod.

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

Den anpassade ReleaseInstance implementeringen lägger till den utgivna instansen tillbaka till poolen och minskar ActiveObjectsCount värdet. EndpointDispatcher kan anropa dessa metoder från olika trådar och därför krävs synkroniserad åtkomst till klassnivåmedlemmarna i ObjectPoolInstanceProvider klassen.

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

Metoden ReleaseInstance tillhandahåller en funktion för att rensa initiering . Normalt underhåller poolen ett minsta antal objekt under poolens livslängd. Det kan dock finnas perioder med överdriven användning som kräver att ytterligare objekt skapas i poolen för att nå den maximala gränsen som anges i konfigurationen. Så småningom när poolen blir mindre aktiv kan dessa överskottsobjekt bli ett extra omkostnader. När når activeObjectsCount noll startas därför en inaktiv timer som utlöser och utför en rensningscykel.

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

ServiceModel-lagertillägg är anslutna med hjälp av följande beteenden:

  • Tjänstbeteenden: Dessa möjliggör anpassning av hela tjänstkörningen.

  • Slutpunktsbeteenden: Dessa möjliggör anpassning av en viss tjänstslutpunkt, inklusive EndpointDispatcher.

  • Kontraktsbeteenden: Dessa möjliggör anpassning av antingen ClientRuntime eller DispatchRuntime klasser på klienten respektive tjänsten.

  • Åtgärdsbeteenden: Dessa möjliggör anpassning av antingen ClientOperation eller DispatchOperation klasser på klienten respektive tjänsten.

För ett tillägg för objektpooler kan antingen ett slutpunktsbeteende eller ett tjänstbeteende skapas. I det här exemplet använder vi ett tjänstbeteende som tillämpar objektpooler för varje slutpunkt i tjänsten. Tjänstbeteenden skapas genom att gränssnittet implementeras IServiceBehavior . Det finns flera sätt att göra ServiceModel medveten om de anpassade beteendena:

  • Använda ett anpassat attribut.

  • Lägga imperativt till tjänstbeskrivningens beteendesamling.

  • Utökar konfigurationsfilen.

Det här exemplet använder ett anpassat attribut. ServiceHost När är konstruerad undersöker den de attribut som används i tjänstens typdefinition och lägger till tillgängliga beteenden i tjänstbeskrivningens beteendesamling.

Gränssnittet IServiceBehavior har tre metoder: Validate,AddBindingParameters, och .ApplyDispatchBehavior Dessa metoder anropas av WCF när ServiceHost initieras. IServiceBehavior.Validate kallas först; det gör att tjänsten kan inspekteras för inkonsekvenser. IServiceBehavior.AddBindingParameters anropas nästa; den här metoden krävs endast i mycket avancerade scenarier. IServiceBehavior.ApplyDispatchBehavior anropas sist och ansvarar för att konfigurera körningen. Följande parametrar skickas till IServiceBehavior.ApplyDispatchBehavior:

  • Description: Den här parametern innehåller tjänstbeskrivningen för hela tjänsten. Detta kan användas för att granska beskrivningsdata om tjänstens slutpunkter, kontrakt, bindningar och andra data som är associerade med tjänsten.

  • ServiceHostBase: Den här parametern innehåller den ServiceHostBase som för närvarande initieras.

I den anpassade IServiceBehavior implementeringen instansen ObjectPoolInstanceProvider instansieras och tilldelas en ny instans av egenskapen InstanceProvider i var och EndpointDispatcher en som är kopplad till ServiceHostBase.

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

Förutom en IServiceBehavior implementering ObjectPoolingAttribute har klassen flera medlemmar för att anpassa objektpoolen med hjälp av attributargumenten. Dessa medlemmar inkluderar MaxSize, MinSizeEnabled och CreationTimeout, för att matcha objektpoolfunktionen som tillhandahålls av .NET Enterprise Services.

Beteendet för objektpooler kan nu läggas till i en WCF-tjänst genom att kommentera tjänstimplementeringen med det nyligen skapade anpassade attributet ObjectPooling .

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

Aktivering och inaktivering av hooking

Det primära målet med objektpooler är att optimera kortlivade objekt med relativt dyr skapande och initiering. Därför kan det ge ett program en dramatisk prestandaökning om det används korrekt. Eftersom objektet returneras från poolen anropas konstruktorn bara en gång. Vissa program kräver dock viss kontrollnivå så att de kan initiera och rensa de resurser som används under en enda kontext. Ett objekt som används för en uppsättning beräkningar kan till exempel återställa sina privata fält innan nästa beräkning bearbetas. Enterprise Services aktiverade den här typen av kontextspecifik initiering genom att låta objektutvecklaren åsidosätta Activate och Deactivate metoder från basklassen ServicedComponent .

Objektpoolen Activate anropar metoden precis innan objektet returneras från poolen. Deactivate anropas när objektet återgår till poolen. Basklassen ServicedComponent har också en boolean egenskap med namnet CanBePooled, som kan användas för att meddela poolen om objektet kan poolas ytterligare.

För att efterlikna den här funktionen deklarerar exemplet ett offentligt gränssnitt (IObjectControl) som har ovan nämnda medlemmar. Det här gränssnittet implementeras sedan av tjänstklasser som är avsedda att ge kontextspecifik initiering. Implementeringen IInstanceProvider måste ändras för att uppfylla dessa krav. Varje gång du hämtar ett objekt genom att anropa GetInstance metoden måste du kontrollera om objektet implementerar IObjectControl. Om det gör det, måste du anropa Activate metoden på rätt sätt.

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

När du returnerar ett objekt till poolen krävs en kontroll för CanBePooled egenskapen innan objektet läggs till i poolen igen.

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

Eftersom tjänstutvecklaren kan bestämma om ett objekt kan poolas kan antalet objekt i poolen vid en viss tidpunkt gå under den minsta storleken. Därför måste du kontrollera om antalet objekt har gått under miniminivån och utföra den nödvändiga initieringen i rensningsproceduren.

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

När du kör exemplet visas åtgärdsbegäranden och svar i både tjänst- och klientkonsolfönstren. Tryck på Retur i varje konsolfönster för att stänga av tjänsten och klienten.

Så här konfigurerar du, skapar och kör exemplet

  1. Kontrollera att du har utfört engångsinstallationsproceduren för Windows Communication Foundation-exempel.

  2. Skapa lösningen genom att följa anvisningarna i Skapa Windows Communication Foundation-exempel.

  3. Om du vill köra exemplet i en konfiguration med en eller flera datorer följer du anvisningarna i Köra Windows Communication Foundation-exempel.