Sdílet prostřednictvím


Sdružování

Ukázka sdružování ukazuje, jak rozšířit Windows Communication Foundation (WCF) na podporu sdružování objektů. Ukázka ukazuje, jak vytvořit atribut, který je syntakticky a sémanticky podobný ObjectPoolingAttribute funkcím atributů podnikových služeb. Sdružování objektů může výrazně zvýšit výkon aplikace. Pokud se však nepoužívá správně, může mít opačný účinek. Sdružování objektů pomáhá snížit režii opětovného vytváření často používaných objektů, které vyžadují rozsáhlou inicializaci. Pokud však volání metody ve fondu objektu trvá poměrně dlouho, než se dokončí, zařadí se do fronty další požadavky, jakmile dojde k dosažení maximální velikosti fondu. Proto může selhat obsluhovat některé požadavky na vytvoření objektu vyvoláním výjimky časového limitu.

Poznámka:

Postup nastavení a pokyny k sestavení pro tuto ukázku najdete na konci tohoto tématu.

Prvním krokem při vytváření rozšíření WCF je rozhodnutí o bodu rozšiřitelnosti, který se má použít.

Ve WCF dispečer výrazu odkazuje na komponentu za běhu zodpovědnou za převod příchozích zpráv na volání metod ve službě uživatele a k převodu vrácených hodnot z této metody na odchozí zprávu. Služba WCF vytvoří dispečer pro každý koncový bod. Klient WCF musí použít dispečer, pokud je kontrakt přidružený k ho klientovi duplexní kontrakt.

Dispečeři kanálů a koncových bodů nabízejí rozšiřitelnost na úrovni kanálu a kontraktu tím, že zveřejňují různé vlastnosti, které řídí chování dispečera. Tato DispatchRuntime vlastnost také umožňuje kontrolovat, upravovat nebo přizpůsobovat proces odesílání. Tato ukázka se zaměřuje na InstanceProvider vlastnost, která odkazuje na objekt, který poskytuje instance třídy služby.

The IInstanceProvider

Ve WCF dispečer vytvoří instance třídy služby pomocí , InstanceProviderkterý implementuje IInstanceProvider rozhraní. Toto rozhraní má tři metody:

Fond objektů

Vlastní IInstanceProvider implementace poskytuje požadovanou sémantiku sdružování objektů pro službu. Proto má tato ukázka ObjectPoolingInstanceProvider typ, který poskytuje vlastní implementaci IInstanceProvider pro sdružování. Dispatcher Při volání GetInstance(InstanceContext, Message) metody, místo vytvoření nové instance, vlastní implementace hledá existující objekt ve fondu v paměti. Pokud je k dispozici, vrátí se. V opačném případě se vytvoří nový objekt. Implementace je GetInstance znázorněna v následujícím ukázkovém kódu.

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

Vlastní ReleaseInstance implementace přidá uvolněnou instanci zpět do fondu a sníží ActiveObjectsCount hodnotu. Tyto Dispatcher metody lze volat z různých vláken, a proto je vyžadován synchronizovaný přístup k členům ObjectPoolingInstanceProvider na úrovni třídy.

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

Metoda ReleaseInstance poskytuje funkci "vyčištění inicializace". Fond obvykle udržuje minimální počet objektů po dobu životnosti fondu. Může však existovat období nadměrného využití, která vyžadují vytvoření dalších objektů ve fondu, aby dosáhla maximálního limitu zadaného v konfiguraci. Když se fond nakonec stane méně aktivní, můžou se tyto nadbytečné objekty stát nadbytečnou režií. Pokud activeObjectsCount tedy dosáhne nuly, spustí se časovač nečinnosti, který aktivuje a provede cyklus čištění.

Přidání chování

Rozšíření dispečerské vrstvy se připojují pomocí následujících chování:

  • Chování služeb. To umožňuje přizpůsobení celého modulu runtime služby.

  • Chování koncového bodu To umožňuje přizpůsobení koncových bodů služby, konkrétně kanálu a dispečeru koncových bodů.

  • Chování kontraktů To umožňuje přizpůsobení obou ClientRuntime tříd DispatchRuntime v klientovi a službě v uvedeném pořadí.

Pro účely rozšíření sdružování objektů je nutné vytvořit chování služby. Chování služby se vytváří implementací IServiceBehavior rozhraní. Existuje několik způsobů, jak model služby upozornit na vlastní chování:

  • Použití vlastního atributu

  • Imperativním přidáním do kolekce chování popisu služby.

  • Rozšíření konfiguračního souboru

Tato ukázka používá vlastní atribut. Při ServiceHost vytvoření prozkoumá atributy použité v definici typu služby a přidá dostupné chování do kolekce chování popisu služby.

IServiceBehavior Rozhraní má v něm tři metody -- Validate, AddBindingParametersa ApplyDispatchBehavior. Metoda Validate se používá k zajištění toho, aby se chování použilo na službu. V této ukázce implementace zajišťuje, že služba není nakonfigurovaná Single. Metoda AddBindingParameters se používá ke konfiguraci vazeb služby. V tomto scénáři se nevyžaduje. Slouží ApplyDispatchBehavior ke konfiguraci dispečerů služby. Tato metoda je volána WCF při ServiceHost inicializaci. Do této metody se předají následující parametry:

  • Description: Tento argument poskytuje popis služby pro celou službu. Můžete ho použít ke kontrole popisových dat o koncových bodech, kontraktech, vazbách a dalších datech služby.

  • ServiceHostBase: Tento argument poskytuje ServiceHostBase právě inicializovaný.

Ve vlastní IServiceBehavior implementaci je vytvořena nová instance instance ObjectPoolingInstanceProvider a přiřazena k InstanceProvider vlastnosti v každé DispatchRuntime v 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;
     }
}

Kromě IServiceBehavior implementace ObjectPoolingAttribute třída má několik členů pro přizpůsobení fondu objektů pomocí argumentů atributu. Mezi tyto členy patří MaxPoolSize, MinPoolSizea CreationTimeout, aby odpovídaly sadě funkcí sdružování objektů poskytované službami .NET Enterprise Services.

Chování při sdružování objektů je teď možné přidat do služby WCF přidáním poznámek k implementaci služby pomocí nově vytvořeného vlastního ObjectPooling atributu.

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

Spuštění ukázky

Ukázka ukazuje výhody výkonu, které je možné získat pomocí sdružování objektů v určitých scénářích.

Aplikace služby implementuje dvě služby – WorkService a ObjectPooledWorkService. Obě služby sdílejí stejnou implementaci – obě vyžadují nákladné inicializace a pak zveřejňují metodu DoWork() , která je relativně levná. Jediným rozdílem je, že ObjectPooledWorkService má nakonfigurované sdružování objektů:

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

Když spustíte klienta, bude volat WorkService 5krát. Pak se 5krát volá ObjectPooledWorkService . Zobrazí se rozdíl v čase:

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.

Poznámka:

Při prvním spuštění obou služeb se zdá, že trvá přibližně stejnou dobu. Pokud ukázku znovu spustíte, uvidíte, že ObjectPooledWorkService vrácení je mnohem rychlejší, protože instance tohoto objektu už ve fondu existuje.

Nastavení, sestavení a spuštění ukázky

  1. Ujistěte se, že jste pro ukázky windows Communication Foundation provedli jednorázovou instalační proceduru.

  2. Pokud chcete sestavit řešení, postupujte podle pokynů v části Sestavení ukázek Windows Communication Foundation.

  3. Pokud chcete spustit ukázku v konfiguraci s jedním nebo více počítači, postupujte podle pokynů v části Spuštění ukázek windows Communication Foundation.

Poznámka:

Pokud použijete Svcutil.exe k opětovnému vygenerování konfigurace pro tuto ukázku, nezapomeňte upravit název koncového bodu v konfiguraci klienta tak, aby odpovídal kódu klienta.