PoolingPooling

В этом примере показано, как расширить Windows Communication Foundation (WCF) для поддержки пула объектов.This sample demonstrates how to extend Windows Communication Foundation (WCF) to support object pooling. Этот образец демонстрирует, как создать атрибут, синтаксически и семантически аналогичный функциям атрибута ObjectPoolingAttribute служб Enterprise Services.The sample demonstrates how to create an attribute that is syntactically and semantically similar to the ObjectPoolingAttribute attribute functionality of Enterprise Services. Использование пулов объектов может значительно повысить производительность приложения.Object pooling can provide a dramatic boost to an application's performance. Однако при неправильном использовании эффект может быть противоположным.However, it can have the opposite effect if it is not used properly. Использование пулов объектов позволяет снизить накладные расходы на повторное создание часто используемых объектов, требующих большого объема инициализации.Object pooling helps reduce the overhead of recreating frequently used objects that require extensive initialization. Однако если завершение вызова метода для объекта из пула занимает много времени, сразу после достижения максимального размера пула функция пулов объектов ставит дополнительные запросы в очередь.However, if a call to a method on a pooled object takes a considerable amount of time to complete, object pooling queues additional requests as soon as the maximum pool size is reached. В результате возможен сбой обслуживания запросов создания некоторых объектов из-за возникновения исключения времени ожидания.Thus it may fail to serve some object creation requests by throwing a timeout exception.

Примечание

Процедура настройки и инструкции по построению для данного образца приведены в конце этого раздела.The setup procedure and build instructions for this sample are located at the end of this topic.

Первым шагом в создании расширения WCF является выбор точки расширения для использования.The first step in creating a WCF extension is to decide the extensibility point to use.

В WCF термин Dispatcher относится к компоненту времени выполнения, который отвечает за преобразование входящих сообщений в вызовы методов в службе пользователя и преобразование возвращаемых значений из этого метода в исходящее сообщение.In WCF the term dispatcher refers to a run-time component responsible for converting incoming messages into method invocations on the user’s service and for converting return values from that method to an outgoing message. Служба WCF создает диспетчер для каждой конечной точки.A WCF service creates a dispatcher for each endpoint. Клиент WCF должен использовать Dispatcher, если контракт, связанный с этим клиентом, является дуплексным контрактом.A WCF client must use a dispatcher if the contract associated with that client is a duplex contract.

Диспетчеры каналов и конечных точек обеспечивают расширяемость на уровне канала и контракта, предоставляя различные свойства, которые управляют поведением диспетчера.The channel and endpoint dispatchers offer channel-and contract-wide extensibility by exposing various properties that control the behavior of the dispatcher. Свойство DispatchRuntime также позволяет контролировать, изменять или настраивать диспетчерский процесс.The DispatchRuntime property also enables you to inspect, modify, or customize the dispatching process. В этом образце рассматривается свойство InstanceProvider, которое указывает на объект, предоставляющий экземпляры класса службы.This sample focuses on the InstanceProvider property that points to the object that provides the instances of the service class.

IInstanceProviderThe IInstanceProvider

В WCF диспетчер создает экземпляры класса службы с помощью InstanceProvider , который реализует IInstanceProvider интерфейс.In WCF, the dispatcher creates instances of the service class using a InstanceProvider, which implements the IInstanceProvider interface. У этого интерфейса есть три метода.This interface has three methods:

Пул объектовThe Object Pool

Пользовательская реализация IInstanceProvider обеспечивает необходимую семантику пула объектов для службы.A custom IInstanceProvider implementation provides the required object pooling semantics for a service. Поэтому в образце имеется тип ObjectPoolingInstanceProvider, который предоставляет пользовательскую реализацию интерфейса IInstanceProvider для создания пула.Therefore, this sample has an ObjectPoolingInstanceProvider type that provides custom implementation of IInstanceProvider for pooling. Когда Dispatcher вызывает метод GetInstance(InstanceContext, Message), пользовательская реализация вместо создания нового экземпляра ищет существующий объект в находящемся в памяти пуле.When the Dispatcher calls the GetInstance(InstanceContext, Message) method, instead of creating a new instance, the custom implementation looks for an existing object in an in-memory pool. Если такой объект доступен, метод возвращает его.If one is available, it is returned. В противном случае создается новый объект.Otherwise, a new object is created. Реализация метода GetInstance показана в следующем образце кода.The implementation for GetInstance is shown in the following sample code.

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

Пользовательская реализация ReleaseInstance добавляет освободившийся экземпляр обратно в пул и уменьшает значение ActiveObjectsCount на единицу.The custom ReleaseInstance implementation adds the released instance back to the pool and decrements the ActiveObjectsCount value. Dispatcher может вызывать эти методы из различных потоков, поэтому требуется синхронизированный доступ к членам уровня класса в классе ObjectPoolingInstanceProvider.The Dispatcher can call these methods from different threads, and therefore synchronized access to the class level members in the ObjectPoolingInstanceProvider class is required.

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

ReleaseInstanceМетод предоставляет функцию "очистить инициализацию".The ReleaseInstance method provides a "clean up initialization" feature. Обычно пул поддерживает минимальное число объектов в течение времени существования пула.Normally the pool maintains a minimum number of objects for the lifetime of the pool. Однако возможны периоды интенсивного использования, когда требуется создавать в пуле дополнительные объекты, пока не будет достигнуто заданное в конфигурации максимальное значение.However, there can be periods of excessive usage that require creating additional objects in the pool to reach the maximum limit specified in the configuration. В конце концов, когда активность пула снизится, эти дополнительные объекты могут стать излишней нагрузкой.Eventually, when the pool becomes less active, those surplus objects can become an extra overhead. Поэтому когда значение activeObjectsCount достигает нуля, запускается таймер бездействия, по истечении времени ожидания которого выполняется цикл очистки.Therefore, when the activeObjectsCount reaches zero, an idle timer is started that triggers and performs a clean-up cycle.

Добавление поведенияAdding the Behavior

Расширения уровня диспетчера подключаются с помощью следующих поведений.Dispatcher-layer extensions are hooked up using the following behaviors:

  • Поведения служб.Service Behaviors. Позволяют настраивать всю среду выполнения службы.These allow for the customization of the entire service runtime.

  • Поведения конечных точек.Endpoint Behaviors. Позволяют настраивать конечные точки службы, включая диспетчера каналов и конечных точек.These allow for the customization of service endpoints, specifically a Channel and Endpoint Dispatcher.

  • Поведения контрактов.Contract Behaviors. Эти поведения позволяют настраивать классы ClientRuntime и DispatchRuntime в клиенте и службе соответственно.These allow for the customization of both ClientRuntime and DispatchRuntime classes on the client and the service respectively.

С целью реализации расширения создания пулов объектов должно быть создано поведение службы.For the purpose of an object pooling extension a service behavior must be created. Поведения служб создаются путем реализации интерфейса IServiceBehavior.Service behaviors are created by implementing the IServiceBehavior interface. Имеется несколько способов сообщить модели службы о пользовательских поведениях:There are several ways to make the service model aware of the custom behaviors:

  • с помощью пользовательского атрибута;Using a custom attribute.

  • путем ее императивного добавления в коллекцию поведений описания службы;Imperatively adding it to the service description’s behaviors collection.

  • путем расширения файла конфигурации.Extending the configuration file.

В этом образце используется пользовательский атрибут.This sample uses a custom attribute. При создании ServiceHost проверяются атрибуты, используемые в определении типа службы, а в коллекцию поведений описания службы добавляются доступные поведения.When the ServiceHost is constructed it examines the attributes used in the service’s type definition and adds the available behaviors to the service description’s behaviors collection.

У интерфейса IServiceBehavior имеется три метода — Validate, AddBindingParameters и ApplyDispatchBehavior.The interface IServiceBehavior has three methods in it -- Validate, AddBindingParameters, and ApplyDispatchBehavior. Метод Validate используется для обеспечения того, что поведение может быть применено к службе.The Validate method is used to ensure that the behavior can be applied to the service. В этом образце реализация обеспечивает, что служба не настраивается с Single.In this sample, the implementation ensures that the service is not configured with Single. Метод AddBindingParameters используется для настройки привязок службы.The AddBindingParameters method is used to configure the service's bindings. Это не требуется в данном сценарии.It is not required in this scenario. Метод ApplyDispatchBehavior используется для настройки диспетчеров службы.The ApplyDispatchBehavior is used to configure the service's dispatchers. Этот метод вызывается WCF при ServiceHost инициализации.This method is called by WCF when the ServiceHost is being initialized. Этому методу передаются следующие параметры.The following parameters are passed into this method:

  • Description: этот аргумент содержит описание службы для всей службы.Description: This argument provides the service description for the entire service. Его можно использовать для проверки данных описания о конечных точках, контрактах и привязках службы, а также других данных.This can be used to inspect description data about the service’s endpoints, contracts, bindings, and other data.

  • ServiceHostBase: этот аргумент содержит инициализируемый в данный момент объект ServiceHostBase.ServiceHostBase: This argument provides the ServiceHostBase that is currently being initialized.

В пользовательской реализации IServiceBehavior создается новый экземпляр ObjectPoolingInstanceProvider, который присваивается свойству InstanceProvider в каждом объекте DispatchRuntime в ServiceHostBase.In the custom IServiceBehavior implementation a new instance of ObjectPoolingInstanceProvider is instantiated and assigned to the InstanceProvider property in each DispatchRuntime in the 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;  
     }  
}  

Помимо реализации интерфейса IServiceBehavior у класса ObjectPoolingAttribute имеется несколько членов для настройки пула объектов с помощью аргументов атрибута.In addition to an IServiceBehavior implementation the ObjectPoolingAttribute class has several members to customize the object pool using the attribute arguments. К этим членам относятся MaxPoolSize, MinPoolSize и CreationTimeout, и они должны соответствовать набору возможностей пула, предоставляемому службами .NET Enterprise Services.These members include MaxPoolSize, MinPoolSize, and CreationTimeout, to match the object pooling feature set provided by .NET Enterprise Services.

Поведение пула объектов теперь можно добавить в службу WCF, заменив в реализации службы только что созданный настраиваемый ObjectPooling атрибут.The object pooling behavior can now be added to a WCF service by annotating the service implementation with the newly created custom ObjectPooling attribute.

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

Запуск примераRunning the Sample

В этом образце демонстрируются преимущества в производительности, которые могут быть получены при использовании пула объектов в различных сценариях.The sample demonstrates the performance benefits that can be gained by using object pooling in certain scenarios.

Приложение службы реализует две службы - WorkService и ObjectPooledWorkService.The service application implements two services -- WorkService and ObjectPooledWorkService. Обе службы совместно используют одну реализацию - обеим требуется обширная инициализация, а затем обе предоставляют метод DoWork(), требующий относительно малых затрат.Both services share the same implementation -- they both require expensive initialization and then expose a DoWork() method that is relatively cheap. Единственное отличие заключается в том, что в службе ObjectPooledWorkService настроено использование пула объектов.The only difference is that the ObjectPooledWorkService has object pooling configured:

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

При выполнении клиента он измеряет время 5-кратного вызова службы WorkService.When you run the client, it times calling the WorkService 5 times. Затем измеряется время 5-кратного вызова службы ObjectPooledWorkService.It then times calling the ObjectPooledWorkService 5 times. Затем отображается разница во времени:The difference in time is then displayed:

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.  

Примечание

При первом запуске клиента обращение к обеим службам занимает приблизительно одинаковое время.The first time the client is run both services appear to take about the same amount of time. При повторном запуске образца видно, что служба ObjectPooledWorkService возвращает результаты намного быстрее, потому что экземпляр этого объекта уже существует в пуле.If you re-run the sample, you can see that the ObjectPooledWorkService returns much quicker because an instance of that object already exists in the pool.

Настройка, сборка и выполнение образцаTo set up, build, and run the sample

  1. Убедитесь, что вы выполнили однократную процедуру настройки для Windows Communication Foundation примеров.Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. Чтобы выполнить сборку решения, следуйте инструкциям в разделе Создание примеров Windows Communication Foundation.To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в разделе выполнение примеров Windows Communication Foundation.To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

Примечание

Если для восстановления конфигурации этого образца используется программа Svcutil.exe, измените имя конечной точки в конфигурации клиента, чтобы оно соответствовало клиентскому коду.If you use Svcutil.exe to regenerate the configuration for this sample, be sure to modify the endpoint name in the client configuration to match the client code.

Важно!

Образцы уже могут быть установлены на компьютере.The samples may already be installed on your machine. Перед продолжением проверьте следующий каталог (по умолчанию).Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

Если этот каталог не существует, перейдите к примерам Windows Communication Foundation (WCF) и Windows Workflow Foundation (WF) для платформа .NET Framework 4 , чтобы скачать все Windows Communication Foundation (WCF) и WFWF примеры.If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WFWF samples. Этот образец расположен в следующем каталоге.This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Instancing\Pooling